API overview
A plain JSON REST API that mirrors the web flow: create a list, get an edit credential back, optionally claim it later. No API key, no signup, no OAuth dance — you can create a list with one curl.
curl -X POST https://onelink.ninja/api/lists
-H 'content-type: application/json'
-d '{"title":"My list","links":[{"url":"https://example.com","label":"Example"}]}' {
"id": "Q20d9AsqNh",
"publicUrl": "https://onelink.ninja/l/Q20d9AsqNh",
"editUrl": "https://onelink.ninja/l/Q20d9AsqNh/edit?token=xK_2_OQe-STVdmd_jiZdUhBoJefLYAmarx",
"editToken": "xK_2_OQe-STVdmd_jiZdUhBoJefLYAmarx",
"markdownUrl": "https://onelink.ninja/l/Q20d9AsqNh.md"
} Full endpoint list in the API reference.
Authentication
Reads are open. Writes need one of two things:
Authorization: Bearer {editToken}— the token returned when the list was created. Works until the list is claimed.- A session cookie owning the list — for a claimed list, from a signed-in browser.
Once a list is claimed its editToken is destroyed and stops working. That is the same rule the web edit page follows.
Hand the editToken back to whoever you made the list for, and tell them it is the only way back in.
Requests
Writes must send content-type: application/json. A form-encoded body is rejected rather than parsed, so a stray HTML form can never reach a write endpoint.
Errors
Every failure has the same shape. Branch on code — the message is prose and may be reworded.
{
"error": {
"code": "invalid_credentials",
"message": "That editToken is not valid for this list."
}
} | code | status | means |
|---|---|---|
invalid_body | 400 | not JSON, wrong content-type, or failed validation |
missing_credentials | 401 | a write with no token and no session |
invalid_credentials | 403 | wrong token, or a session that does not own the list |
not_found | 404 | no list with that id |
rate_limited | 429 | too many creates from your address |
On a validation failure, error.details carries the specific field problems.
Note that a wrong credential is a 403, not a 404. Whether a list exists is already public via GET, so pretending otherwise would only make your own bugs harder to find.
Rate limits
Creating a list is limited to 10 per minute per IP — generous for a person, dull for a script. A 429 means wait, not that the request was malformed; the message tells you for how long. Successful creates carry X-RateLimit-Remaining.
Nothing else is limited: the other endpoints need a credential that had to be handed out, or are reads.
CORS
Every endpoint sends Access-Control-Allow-Origin: * and answers preflight, so browser extensions and bookmarklets work from any origin.
There is deliberately no Access-Control-Allow-Credentials. Browsers refuse to send cookies to a wildcard origin, which means another site cannot use a visitor’s signed-in session to edit their lists. Cross-origin callers authenticate with a Bearer token instead — one they can only have if somebody gave it to them.
onelink