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
- Open Settings → API keys in the dashboard and hit New key.
- 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.
- Send it as a bearer token on every request:
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:
If that returns 200, your key is valid, unrevoked, and your plan still includes the API.
What you can do
| Area | Endpoints |
|---|---|
| Links | GET/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 outcomes | GET /v1/links/{id}/stats · /clicks · /ab-stats |
| Analytics | GET /v1/analytics · GET /v1/analytics/export |
| Organization | /v1/folders · /v1/pixels |
| Bio page | GET/PUT/DELETE /v1/bio · PUT /v1/bio/blocks · GET /v1/bio/stats |
| Audience | GET /v1/audience · DELETE /v1/audience/{id} · /export.csv |
| Webhooks | GET/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.
Create a link
url is the only required field. Everything else is optional:
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:
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.
These numbers are sampled — treat them as trends
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:
- Round to whole numbers. A percentage with decimals is false precision.
appOpenRateisnull— never0— when nothing has been measured yet. Branch onnullas "no data", not as "nobody opened it".configured: falsemeans 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.
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:
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 is a stable machine-readable code you can branch on; message is for humans.
| Status | What it means |
|---|---|
400 | Invalid body. A validation failure adds an issues array. |
401 | Missing, malformed, unknown or revoked key — or your plan no longer includes the API. |
403 | A plan gate. error names the feature (ab_testing, bulk_create, read_only, …). |
404 | Not found, or not yours. A key can only ever see its own workspace. |
409 | Conflict — a taken slug or handle. |
429 | Rate limited. Wait Retry-After seconds. |
Your links are permanent
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.
Related
- MCP server — the same surface, exposed to Claude/Cursor as tools so you can ask for a link instead of writing the request. Same key, same rules.
- Webhooks — the push version of this API. Use it instead of polling.
- n8n — this API as workflow steps, plus the trigger that fires on whether the app actually opened.
- ManyChat — one External Request block against
POST /v1/links, so a comment-to-DM flow can mint its own link. - The app-open receipt and your media kit — the same outcome numbers rendered for humans instead of for a script.
- Link in bio —
/v1/biois this page, editable from a script. - Blog: Every link tool has an API. This one can tell your automation whether the app opened.
- Changelog: The public API
- A/B testing and routing — the link powers most worth driving from a script.