ZIPPY DOCS

API

The Zippy public API — create and edit links, read your click and app-open numbers, manage your bio page and audience, all with a zip_ key. Built for n8n, Make, Zapier, and anything that speaks HTTP.

The Zippy API

Everything you can do in the Zippy dashboard, you can do from a script — including the one thing no other link tool can hand an automation: did the tap actually open the app?

The API lives at https://api.zipthe.link/v1. It's a Legend power.

Get a key

  1. Open Settings → API keys in the dashboard and hit New key.
  2. Copy it. Zippy shows the key exactly once — only a hash is stored, so it cannot be read back later. Lost it? Revoke it and mint a new one.
  3. Send it as a bearer token on every request:
curl https://api.zipthe.link/v1/me \
  -H "authorization: Bearer zip_your_key_here"

Keys start with zip_. Treat one like a password: it can create and edit every link in your workspace. Revoking a key kills it instantly — the very next request with it gets a 401.

Cookies don't work here, on purpose. /v1 reads the Authorization header and nothing else. Being logged into the dashboard in the same browser does not authenticate an API call. That's what makes it safe to point a third-party automation at it.

Check it works

GET /v1/me is the cheapest call in the API and the one to use as a "test connection" step:

{
  "orgId": "org_…",
  "tier": "legend",
  "features": { "api": true, "fullAnalytics": true,  },
  "rateLimit": { "limit": 120, "windowSeconds": 60 }
}

If that returns 200, your key is valid, unrevoked, and your plan still includes the API.

What you can do

AreaEndpoints
LinksGET/POST /v1/links · GET/PATCH /v1/links/{id} · POST /v1/links/bulk · POST /v1/links/{id}/archive
Link extras/qr · /og (get, override, re-scrape) · /receipt
App-open outcomesGET /v1/links/{id}/stats · /clicks · /ab-stats
AnalyticsGET /v1/analytics · GET /v1/analytics/export
Organization/v1/folders · /v1/pixels
Bio pageGET/PUT/DELETE /v1/bio · PUT /v1/bio/blocks · GET /v1/bio/stats
AudienceGET /v1/audience · DELETE /v1/audience/{id} · /export.csv
WebhooksGET/POST /v1/webhooks · PATCH/DELETE /v1/webhooks/{id}

The complete, always-current reference — every field, every error — is the OpenAPI document at /openapi.json, browsable at /docs. Point a client generator at it and you have a typed SDK in any language.

url is the only required field. Everything else is optional:

curl -X POST https://api.zipthe.link/v1/links \
  -H "authorization: Bearer zip_…" \
  -H "content-type: application/json" \
  -d '{
    "url": "https://www.instagram.com/reel/abc123",
    "slug": "summer-drop",
    "tags": ["campaign"],
    "utm": { "source": "newsletter", "medium": "email" }
  }'

The full create body covers every power on the link editor — routing, ab, sched, password, utm, tags/folderId, pixelIds and captureEmails. Each is gated by your plan on its own: if your tier doesn't include one, you get a 403 naming that feature rather than a link that silently ignored what you asked for.

Editing works the same way — PATCH /v1/links/{id} takes the same fields. Sending null clears a field; sending an array or object replaces it wholesale.

The interesting part: app-open outcomes

GET /v1/links/{id}/stats is the endpoint worth building an automation around:

{
  "configured": true,
  "total": 1840,
  "appOpenRate": 0.87,
  "outcomes": { "opened": 1601, "browser": 205, "broken": 34 }
}
  • opened — the destination app actually came to the foreground.
  • browser — the tap landed in an in-app or mobile browser instead.
  • broken — the hand-off to the app failed.

Click counts tell you a tap happened. This tells you whether it worked — which is something you can branch on. "If appOpenRate drops below 0.7 on my TikTok link, post to Slack" is a two-step automation here and impossible anywhere else.

GET /v1/links/{id}/clicks gives you the same signal as individual events (time, outcome, source app, country, device), and /ab-stats splits it per A/B variant, so you can pick the variant that opens the app rather than the one that merely gets taps.

Outcome and click numbers come from a sampled analytics pipeline. They're accurate enough to steer decisions and not exact enough to reconcile against a ledger. Three rules if you're building on them:

  1. Round to whole numbers. A percentage with decimals is false precision.
  2. appOpenRate is null — never 0 — when nothing has been measured yet. Branch on null as "no data", not as "nobody opened it".
  3. configured: false means the upstream pipeline isn't reporting. Those zeros are placeholders, not a real "no traffic" answer. Don't chart them.

Pagination

List endpoints take ?limit (max 200) and ?offset. Page until you get back fewer rows than you asked for. Omit both and you get the full list.

GET /v1/links?limit=100&offset=0
GET /v1/audience?limit=100&offset=100

On /v1/audience, count is your org total, not the size of the page.

Rate limits

120 requests per minute, per key. Every response carries the standard headers:

X-RateLimit-Limit: 120
X-RateLimit-Remaining: 118
X-RateLimit-Reset: 1784500000

Go over and you get a 429 with a Retry-After header (in seconds) and a body telling you how long to wait. Honour it — retrying immediately just burns the next window.

If you're polling this API on a timer to spot new events, stop and use webhooks instead. Zippy pushes link and app-open events to your URL the moment they happen; an idle automation should make zero requests to the API.

Errors

Every error, on every endpoint, is the same shape:

{ "error": "read_only", "message": "This link is read-only on your current plan." }

error is a stable machine-readable code you can branch on; message is for humans.

StatusWhat it means
400Invalid body. A validation failure adds an issues array.
401Missing, malformed, unknown or revoked key — or your plan no longer includes the API.
403A plan gate. error names the feature (ab_testing, bulk_create, read_only, …).
404Not found, or not yours. A key can only ever see its own workspace.
409Conflict — a taken slug or handle.
429Rate limited. Wait Retry-After seconds.

There is no endpoint on this API that stops a published link from redirecting. archive removes a link from your list and frees a slot against your plan's link cap — the link keeps resolving. Downgrading makes links read-only, never dead. Build on that: a link you create through the API today will still work years from now.

FAQ

How do I get a Zippy API key?

Settings → API keys → New key in the dashboard, on a Legend workspace. The key is shown once and stored only as a hash, so it can't be read back — revoke and mint a new one if you lose it. Send it as Authorization: Bearer zip_….

Can an API tell me whether someone opened my app?

Zippy's can. GET /v1/links/{id}/stats returns appOpenRate and the opened / browser / broken split for every tap on a link, and /ab-stats splits it per A/B variant. A click-count API can't tell a tap that reached the app from one that died in an in-app browser — to it they're the same event.

How do I connect Zippy to n8n, Make, or Zapier?

Point an HTTP Request node at https://api.zipthe.link/v1 with your zip_ key as a bearer token, and use GET /v1/me as the connection test. To receive events, register a webhook with your tool's catch-hook URL instead of polling on a timer.

Are the API's numbers exact?

No. Click and outcome numbers are sampled estimates — round them, and treat appOpenRate: null as "nothing measured yet", never as zero.