Free plan

Authenticating with the API

How to add credentials to API requests and understand the error responses the API returns.

The EveryPage API uses Bearer token authentication: include your API key or OAuth token in the Authorization header of every request. The /api/v1/ prefix is not just a version marker—it is what selects the permissive, credential-less CORS policy (so the API is callable from any origin) and the exemption from the same-origin CSRF check. Nothing under /api/v1/ authenticates by session cookie.

Bearer tokens and token lanes

The API accepts two kinds of bearer tokens, distinguished by their prefix:

API keys begin with ep_live_ and resolve to the wildcard scope *, so they pass every scope check and carry full account access. They cannot be narrowed. Use them for your own scripts and server-side integrations.

OAuth access tokens begin with ep_at_ and carry only the scopes granted at authorization. Use them for third-party apps whose access should be limited—for example, an integration that only uploads documents holds files:write and not readership:read.

Both arrive in the same Authorization: Bearer <token> header, and the prefix is what selects the lane. A token with neither prefix, or one that is unknown, revoked, or expired, is rejected with 401—there is no fallback that could grant unintended access.

Making a request

curl https://everypage.co/api/v1/files \
  -H "Authorization: Bearer ep_live_..."

GET /api/v1/files lists the files on your account and needs the files:read scope. Replace ep_live_... with your own key; OAuth tokens use the same header with the ep_at_... prefix.

HTTP status codes

The API returns standard HTTP status codes. This table covers the most common ones and what they mean in this API:

CodeNameMeaning
200OKRequest succeeded. The response body carries the result. This includes POST /api/v1/files and POST /api/v1/files/import—uploads answer 200, not 201.
201CreatedReturned by POST /api/v1/webhooks and POST /api/v1/files/{uuid}/variants.
400Bad RequestThe request is malformed or violates a constraint (e.g. not a PDF, malformed body, unknown type on the events feed).
401UnauthorizedThe Authorization header is missing or malformed, or the token is unknown, revoked, or expired.
403ForbiddenYour OAuth token lacks the required scope (insufficient_scope: requires <scope>); your plan does not include the feature (This feature requires the pro plan or higher); or you do not own the target file on a write or readership endpoint.
404Not FoundThe identifier does not resolve. It is also returned deliberately in place of 403 where a 403 would confirm that a document exists: GET /api/v1/files/{uuid} answers 404 for a file you do not own, and the manage-token settings and content lane answers 404 once a file has been claimed. Note the asymmetry—write and readership endpoints answer 403 for a file owned by someone else.
409ConflictThe request conflicts with existing state: the slug you asked for is already used by another of your files, the file was already claimed by a different account, or a concurrent change invalidated a design-lane republish (retry).
410GoneThe link is dead—expired, or burned by its view limit. Both refuse settings changes. POST /api/v1/files/{uuid}/content refuses only expired links: replacing the content is the documented way to un-burn a link.
413Payload Too LargeThe upload or converted result exceeds your plan's size limit.
429Too Many RequestsRate limit exceeded. Wait before retrying.

Send Content-Type: application/json with JSON request bodies; multipart file uploads (e.g. POST /api/v1/files) use Content-Type: multipart/form-data instead.

Identifying documents

Endpoints that address a specific document accept either identifier in the path—both resolve to the same file:

  • UUID: the canonical xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx identifier
  • Short ID: a 12-character base62 code (e.g. aB3xY9kQ2mZ7) that appears in share links

Vanity slugs are not accepted by the API. A slug resolves only on its owner's active custom domain, on the viewer surface; API calls must use the UUID or the short ID. See Share links and short IDs for the identifier rules.

Rate limiting

Requests to /api/v1/ are limited to 120 per minute, keyed on the value of the Authorization header—so the bucket is per token, and integrations sharing an egress IP do not share a budget. Over the limit the API returns HTTP 429.

POST /api/v1/webhooks/{uuid}/test carries an additional 10-per-minute limit on the same key.

If a 429 is returned, wait before retrying; exponential backoff (e.g. 1s, 2s, 4s) is a reasonable strategy.

Error responses

Every error under /api/v1/ is an RFC 9457 Problem Details document with Content-Type: application/problem+json. Requests to unknown paths under /api/ get the same shape. For example:

{
  "type": "https://everypage.co/docs/developers/authentication#insufficient-scope",
  "title": "Forbidden",
  "status": 403,
  "detail": "insufficient_scope: requires files:write",
  "instance": "https://everypage.co/api/v1/files",
  "code": "insufficient_scope",
  "hint": "Reconnect with a token that grants files:write. API keys (ep_live_) hold every scope."
}

Branch on status or code, never on detail. detail is the human-readable message and may be reworded; code is stable. type links to the matching section below, instance is the request URL, and hint says what to do next. Rate-limited responses also carry Retry-After and the X-RateLimit-* headers; 401 and 403 responses carry an RFC 6750 WWW-Authenticate: Bearer … challenge.

Codes not listed here are derived from the HTTP reason phrase in lower snake case (for example precondition_required) and may be added without notice.

Missing token

401, code missing_token. No Authorization header, or one that is not Bearer <token>. The response carries WWW-Authenticate: Bearer realm="EveryPage API". Create an API key under Account → API keys.

Invalid token

401, code invalid_token. The token is unknown, revoked, or expired. The challenge is WWW-Authenticate: Bearer realm="EveryPage API", error="invalid_token". Rotate the key, or refresh the OAuth access token.

Insufficient scope

403, code insufficient_scope. An OAuth token without the scope the route needs; detail names it (insufficient_scope: requires files:write) and the challenge is WWW-Authenticate: Bearer error="insufficient_scope", scope="files:write". API keys hold every scope and never see this error.

Plan required

403, code plan_required. The feature is above the account's plan; detail names the plan (This feature requires the pro plan or higher). Upgrade at Pricing.

Forbidden

403, code forbidden. Any other refusal: not the owner, a plan limit reached (file count, webhook count), or a manage token used on a plan-gated field.

Validation error

400, code validation_error. Malformed JSON, a missing field, or an invalid value; detail says which.

Not found

404, code not_found. Unknown id, or a file you do not own on routes where 404 replaces 403 to avoid leaking existence. Ids are the file UUID or its 12-character short id.

Method not allowed

405, code method_not_allowed. The Allow header lists the supported methods.

Conflict

409, code conflict. A vanity slug already in use, a file already claimed by another account, or a concurrent content replacement.

Gone

410, code gone. The link is dead: expired or burned. Settings cannot revive it; a burned link is un-burned by replacing its content.

Payload too large

413, code payload_too_large. The upload, the converted PDF, or a JSON body is above the limit for your plan.

Unsupported media type

415, code unsupported_media_type. Reserved; no route returns it today (an unrecognised upload is a 400).

Unprocessable

422, code unprocessable. A Word or PowerPoint file could not be converted to PDF.

Rate limited

429, code rate_limited. Over the 120-per-minute budget for this token (10 per minute on webhook tests). Wait for the number of seconds in Retry-After before retrying.

Internal error

500, code internal_error. Something failed on our side; retrying is safe for reads. Please report it with the instance URL.

Service unavailable

503, code service_unavailable. A dependency (the document converter, Canva key verification) is down. Retry with backoff.

Outside /api/v1/ the shapes differ: /oauth/token returns the RFC 6749 { "error": "…", "error_description": "…" } object, and the public oEmbed and QR endpoints return plain text.

API reference

This article covers the conventions common to all routes. For the endpoint listing, see:

Next steps