REST API
Create links, Smart Links, forms and Multilink pages from your own code, and read their analytics, with the same rules as the Walink app.
The Walink API lets a script, a backend or any no-code tool that sends HTTP requests do what you do in the app. Every request runs through the app's own rules, so addresses, allowance and ownership are checked the same way, and every create makes a draft, except a free link, so nothing goes live by accident.
This page gets you to a first call. Every endpoint and field is in the full API reference, which also has a console to try requests.
What you can automate
| Resource | What the API does |
|---|---|
Premium Links (/v1/links) |
List, create drafts, edit, publish, unpublish, delete, style the QR |
Smart Links (/v1/smart-links) |
The same, with destinations and routing |
Forms (/v1/forms) |
The same, plus the responses people sent |
Multilink pages (/v1/multilinks) |
The same |
Free links (/v1/free-links) |
List, create, read, delete |
Analytics (/v1/analytics) |
Figures and breakdowns by country, source, device and more |
Account (/v1/account) |
Profile, allowance in use, free links held |
Any Walink account can use the API. Drafts are free. Publishing uses your subscription's allowance and analytics need a subscription, as in the app.
Create a token
- Sign in to Walink, open the account menu (the round button at the top right) and select Developers.
- Under API Tokens, select Create token.
- Give it a Name, such as "Reporting script", and pick when it Expires after: 30 days, 90 days, 1 year (the default) or No expiration.
- Under Scopes, tick only what the token needs. Quick presets fills in Read only or Full access. Scopes can't be changed later.
- Select Create token. The Save your token window shows it once: copy it somewhere safe, such as a password manager, then select I've saved it.
Tokens start with wlk_live_, and an account can hold 25 active tokens. In the tokens table, Rotate gives you a new token with the same name and scopes while the old one keeps working for 7 days. Revoke stops a token for good, within 30 seconds.
Scopes
write includes read. Nothing includes publish, delete or responses:read: each is granted on its own, and none is on by default.
| Scope | Allows |
|---|---|
links:read |
Read Premium Links, Smart Links and free links |
links:write |
Create and edit drafts |
links:publish |
Publish, unpublish, edit anything published, create free links |
links:delete |
Delete |
forms:*, multilinks:* |
The same four for forms and Multilink pages |
account:read |
Profile, allowance and usage |
analytics:read |
Analytics |
responses:read |
Form responses, which hold personal data people sent you |
analytics:read and multilinks:read also show the names of your published links, Smart Links and forms, even without their own read scope.
Your first request
The base URL is https://api.walink.com/v1. Send the token as Authorization: Bearer <token> and request bodies as JSON. The examples read the token from a WALINK_API_TOKEN environment variable, so it stays out of your code.
List your Premium Links (needs links:read):
curl https://api.walink.com/v1/links \
-H "Authorization: Bearer $WALINK_API_TOKEN"
{
"data": [
{
"id": "lnk_3f2a9c1e-7b4d-4e8a-9f10-2c6d8e4b1a77",
"title": "Tienda Luna orders",
"handle": "tienda-luna",
"plan": "chat",
"status": "published",
"url": "https://wa.link/tienda-luna",
"created_at": "2026-11-05T14:32:07.000Z",
"updated_at": "2026-11-05T14:32:07.000Z"
}
],
"has_more": false,
"next_cursor": null
}
Lists are newest first. They take limit (25 by default, 100 at most) and cursor: pass next_cursor to get the next page.
Create a draft
A create always makes a draft (needs links:write). This one opens a chat with a US number:
curl -X POST https://api.walink.com/v1/links \
-H "Authorization: Bearer $WALINK_API_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: tienda-luna-promo-1" \
-d '{
"handle": "tienda-luna-promo",
"plan": "chat",
"title": "Tienda Luna promo",
"phone": "4155550123",
"country_code": "1",
"country_alpha": "US",
"message": "Hello Tienda Luna, I saw your promo"
}'
The answer is 201 with the full record, its id and "status": "draft".
handleis the address afterwa.link/. It can't be changed after the link is created, and a draft doesn't reserve it: the first account to publish on it gets it.planis the link type:chat,catalog,channelorgroup.Idempotency-Keymakes a retry safe for 24 hours: the same key and body return the first answer instead of a second draft.
Publish
Publishing is its own call, and it needs links:publish:
curl -X POST https://api.walink.com/v1/links/lnk_3f2a9c1e-7b4d-4e8a-9f10-2c6d8e4b1a77/publish \
-H "Authorization: Bearer $WALINK_API_TOKEN"
Like Publish in the app, it uses one unit of your Premium Links allowance ($9 a year per link). Without a free unit the answer is 402 pool_exhausted, with the numbers in details. Add allowance on the app's Billing page (how billing works).
POST .../publish-checkis a dry run: what still blocks the publish and how much allowance it would use.POST .../unpublishtakes the link offline and frees its unit. Its address stops working, printed QR codes included, and another account can then claim it.PATCH /v1/links/{id}changes only the fields you send. On a published link the change is live at once, so it needslinks:publishtoo.
Smart Links, forms and Multilink pages follow the same steps under their own paths. Smart Links use the links scopes.
Free links on the API
/v1/free-links makes the free links every account can hold, up to 5. They work differently:
- A free link is live the moment it's created, never a draft. That's why creating one needs
links:publish, notlinks:write. - Walink picks its random address. You send a phone number or WhatsApp username, an optional message and an optional private
title, which can't be changed later. - It can be listed, read and deleted, never edited. Its id starts with
fln_, and/v1/linksnever shows it. - Editing it or seeing its analytics needs an upgrade to Premium in the app, which changes its id to
lnk_.
With 5 already, a create answers 409 free_link_limit_reached, and deleting one frees its slot. More in free links.
Errors and rate limits
Every error has the same shape:
{
"error": {
"code": "insufficient_scope",
"message": "This token needs the links:publish scope.",
"param": null,
"doc_url": "https://api.walink.com/docs#description/errors",
"request_id": "req_4f1c9a0b7d2e4c8f9a1b3c5d7e9f0a2b",
"details": { "required": ["links:publish"] }
}
}
| Status | Common codes | Meaning |
|---|---|---|
| 401 | invalid_token |
Missing, expired or revoked token |
| 402 | pool_exhausted, past_due_locked, subscription_required |
Allowance or billing, fixed in the app |
| 403 | insufficient_scope |
details.required names the scope |
| 404 | not_found |
Also for ids on other accounts |
| 409 | handle_taken, free_link_limit_reached |
The address is taken, or a limit is hit |
| 422 | validation_failed, handle_immutable |
Invalid request. details.fields lists the problems |
| 429 | rate_limited |
Wait Retry-After seconds |
| Requests | Limit |
|---|---|
| Reads | 120 a minute per token |
| Creates, edits, publishing and deletes | 30 a minute per account |
| Analytics | 10 a minute per account |
| Address checks | 30 a minute per account |
The limits are approximate, and running out of allowance is never a 429. Through the API, an account can hold up to 500 drafts per product. Every response carries a Request-Id header: quote it when you contact support.
Full reference
- API reference: every endpoint and field, with a console.
- OpenAPI 3.1 document and enums and limits, no token needed.
- llms.txt: the API's rules, for AI tools.
The same token connects AI assistants too: see Connect AI assistants.