API overview
How to authenticate to the Fortly HTTP API, what requests and responses look like, and how errors and limits are reported.
The Fortly API is plain HTTPS and JSON. It is the same API the console uses — every screen in the console is a view over these routes, so anything you can do there you can script here. This page covers what every route has in common; the pages under it list each route, its parameters and what it returns.
Base URL
https://api.fortly.comAll paths in this reference are relative to it. There is no version prefix.
Authentication
Send an API token as a bearer token on every request:
GET /projects HTTP/1.1
Host: api.fortly.com
Authorization: Bearer fly_...Make a token in the console under Settings › API tokens. It is shown once, when it is made; Fortly stores only a digest of it. See API tokens for expiry, renewal and rotation.
export FORTLY_TOKEN=fly_...
curl https://api.fortly.com/projects \
-H "Authorization: Bearer $FORTLY_TOKEN"const res = await fetch("https://api.fortly.com/projects", {
headers: { Authorization: `Bearer ${process.env.FORTLY_TOKEN}` },
});
const { projects } = await res.json();A token acts as the account that made it, with that account's access: its own projects and those of any team it belongs to. A missing token, or one that is expired or revoked, gets 401.
| Situation | Status | error |
|---|---|---|
No Authorization: Bearer header | 401 | sign in first |
| Token unknown, expired or revoked | 401 | that token is not valid, or has expired |
| Fortly could not check the token | 503 | could not check your session |
A fly_ token can do anything its account can, including deleting projects. Keep it in server-side code and CI secrets, never in a browser. For a frontend, use the project's publishable cvp_ key with the frontend data API.
Project names in paths
Most routes are under /projects/{name}. {name} is the project's name field, which is the name you chose joined to your account's slug: a project you create as my-app gets the name my-app-ada if your slug is ada. The name you chose is returned as label. Read name from List projects or from the response to Create a project.
A project you cannot access answers 404 with no project called {name} — the same answer as a project that does not exist, so a name is never confirmed to someone outside it.
Requests
- JSON bodies must be sent with
Content-Type: application/json. Without it the request is refused with415; a body that is not valid JSON is400, and one that is missing a required field or has the wrong type is422. - Binary bodies — a release archive, an uploaded file, a SQL import — are sent raw as the request body, not as a multipart form. Use
--data-binary @filewith curl. - Body size. JSON bodies are limited to 2 MB. The routes that take files have their own ceilings: 256 MB for a release, 64 MB for an uploaded file and 256 MB for a SQL import. A body over the limit is refused with
413. - Timestamps in requests and responses are Unix seconds (integers), UTC.
Responses
Successful responses are JSON with 200, 201 or 202, except:
- Deletions answer
204 No Contentwith an empty body. - Export the database answers with the file itself and a
Content-Dispositionheader naming it.
Lists are wrapped in an object named for what they hold — { "projects": [...] }, { "deploys": [...] } — so a field can be added beside the list later without breaking you.
Errors
An error from Fortly is a JSON object with an error string written to be shown to a person:
{ "error": "you already have a project called my-app" }Some errors carry one more field that helps you recover; each route documents its own. For example, a name clash on Create a project also returns a free suggestion.
| Status | Meaning |
|---|---|
400 | The request was understood and refused. error says why, in the database's own words where the database refused it. |
401 | No valid token. |
403 | Your plan does not allow it, or your role in the team does not. |
404 | The project, release, domain, file or schedule does not exist, or is not yours. |
409 | It conflicts with something that exists: a project name you already use, a domain already on a project, a release already building. |
410 | A release that can no longer be made live. |
413 | The body is over the route's size limit. |
415, 422 | A JSON body without Content-Type: application/json, or with the wrong shape. These come from the HTTP layer as plain text, not JSON. |
429 | Too many requests of one kind. See Rate limits. |
500 | Something on Fortly's side failed. |
501 | The platform is not configured for this feature, such as storing secrets. |
502, 503 | An upstream Fortly depends on — DNS, object storage, the node itself — did not answer. Safe to retry. |
Rate limits
There is no general request quota. Creating a project is throttled per account, because every project claims a database on a shared server: it allows a burst of 8 and then one more every 5 seconds. A throttled request answers 429 with a Retry-After header in seconds:
HTTP/1.1 429 Too Many Requests
Retry-After: 5
Content-Type: application/json
{"error":"Projects are being created too quickly on this account. Wait a moment and try again."}Pagination
Lists return everything there is, except where a route says otherwise:
- Read rows takes
limitandoffsetand returns atotal. - List releases returns the 50 newest, and List releases across projects the 100 newest.
Retrying
There is no idempotency-key header. What a retry does depends on the route:
- Creating something with a name — a project, a secret, a domain — is safe to retry. A second project with the same name answers
409; a secret with the same name is replaced; a domain already added answers409. - Uploading a release twice makes two releases. The second becomes live, which is harmless when the archive is the same.
- Updates and deletions by name or id are safe to repeat; a second deletion answers
404.
Resources
The routes above are for managing a project from outside it. From inside a running project, your server reads and writes its own database through the server data API, and a browser through the frontend data API.