# API overview

> How to authenticate to the Fortly HTTP API, what requests and responses look like, and how errors and limits are reported.

Source: https://docs.fortly.com/api

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

```text
https://api.fortly.com
```

All 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:

```http
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](https://docs.fortly.com/account/tokens) for expiry, renewal and rotation.

**curl**

```bash
export FORTLY_TOKEN=fly_...

curl https://api.fortly.com/projects \
  -H "Authorization: Bearer $FORTLY_TOKEN"
```

**JavaScript**

```js
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` |

> **Warning:**
>
> 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](https://docs.fortly.com/data/frontend-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](https://docs.fortly.com/api/projects#list-projects) or from the response to [Create a project](https://docs.fortly.com/api/projects#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 with `415`; a body that is not valid JSON is `400`, and one that is missing a required field or has the wrong type is `422`.
- **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 @file` with curl.
- **Body size.** JSON bodies are limited to 2 MB. The routes that take files have their own ceilings: 256 MB for a [release](https://docs.fortly.com/api/deploys#upload-a-release), 64 MB for an [uploaded file](https://docs.fortly.com/api/files#upload-a-file) and 256 MB for a [SQL import](https://docs.fortly.com/api/data#import-a-sql-script). 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 Content` with an empty body.
- [Export the database](https://docs.fortly.com/api/data#export-the-database) answers with the file itself and a `Content-Disposition` header 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:

```json
{ "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](https://docs.fortly.com/api/projects#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](#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](https://docs.fortly.com/api/projects#create-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
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](https://docs.fortly.com/api/data#read-rows) takes `limit` and `offset` and returns a `total`.
- [List releases](https://docs.fortly.com/api/deploys#list-releases) returns the 50 newest, and [List releases across projects](https://docs.fortly.com/api/deploys#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 answers `409`.
- 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

- [Projects](https://docs.fortly.com/api/projects): List, create, configure and delete projects.
- [Deploys](https://docs.fortly.com/api/deploys): Upload a release, watch it build, and roll back.
- [Data](https://docs.fortly.com/api/data): Tables, rows, schema changes, SQL, import and export.
- [Secrets](https://docs.fortly.com/api/secrets): Environment values, encrypted at rest.
- [Domains](https://docs.fortly.com/api/domains): Add a domain, verify it, and see its certificate.
- [Schedules](https://docs.fortly.com/api/schedules): Request a path on your project on a cron timer.
- [Files](https://docs.fortly.com/api/files): Upload, rename and remove stored files.

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](https://docs.fortly.com/data/server-api), and a browser through the [frontend data API](https://docs.fortly.com/data/frontend-api).
