---
title: Developer API
description: "The Amplitude Developer API is an OpenAPI-defined REST API for projects, taxonomy, feature flags, analytics, destinations, and implementation skills."
product: general
token_estimate: 1550
---
# Developer API

> For AI agents: a documentation index is available at [/docs/llms.txt](/docs/llms.txt). Append `.md` to any page URL for markdown, or send `Accept: text/markdown`.

The Amplitude Developer API is the OpenAPI-defined REST API for projects, taxonomy, feature flags, analytics, destinations, and implementation skills. It's the contract behind the [amp CLI](https://amplitude.com/docs/developers/cli).

## Base URLs

| Region | Base URL |
| --- | --- |
| United States | `https://developer-api.amplitude.com` |
| Europe | `https://developer-api.eu.amplitude.com` |

## Authentication

Most Developer API requests require an HTTP `Bearer` token. Send it in the `Authorization` header:

```bash
Authorization: Bearer YOUR_ACCESS_TOKEN
```

A Bearer token is either an OAuth access token from the device flow or a Personal Access Token (PAT). Both use the same header and scope model, and either can call the endpoints its scopes allow. The device authorization, token exchange, and skills endpoints don't require a Bearer token.

Call [Get authenticated context](https://amplitude.com/docs/apis/developer/projects/get-authenticated-context) to check which credential type, scopes, and organization a token resolves to. Its `principal.auth_type` field returns `oauth`, `pat`, `service_account`, or `api_key`.

### OAuth device flow

Use the device flow to authorize a CLI, script, or other input-constrained client without embedding a client secret. The flow has two steps: start a device authorization request, then poll for a token.

1. **Start a device authorization request.** Send the space-delimited `scope` you need to the device-authorization endpoint. This endpoint doesn't require a Bearer token.

   ```bash
   curl -X POST https://developer-api.amplitude.com/v1/auth/device-authorization \
     -H "Content-Type: application/json" \
     -d '{
       "scope": "projects:read analytics:read"
     }'
   ```

   The response includes `device_code`, `user_code`, `verification_uri` (and `verification_uri_complete`), `expires_in`, and a polling `interval`.
2. **Direct the user to approve the request.** Show the `user_code` and send the user to `verification_uri` (or open `verification_uri_complete` directly), where they approve the request in the browser.
3. **Poll for a token.** While the user approves the request, poll the token endpoint with the `device_code` grant:

   ```bash
   curl -X POST https://developer-api.amplitude.com/v1/auth/token \
     -H "Content-Type: application/json" \
     -d '{
       "grant_type": "urn:ietf:params:oauth:grant-type:device_code",
       "device_code": "YOUR_DEVICE_CODE"
     }'
   ```

   Poll no more often than the `interval` from step 1. Once the user approves the request, the token endpoint returns `access_token`, `token_type`, `expires_in`, `refresh_token`, `scope`, and `id_token`. Send `access_token` as the Bearer token on subsequent requests.

### Personal access tokens

A Personal Access Token (PAT) is a long-lived Bearer credential for CI pipelines and other non-interactive environments where running the device flow isn't practical. Send a PAT in the `Authorization` header the same way as an OAuth access token. The [amp CLI](https://amplitude.com/docs/developers/cli) accepts an existing PAT non-interactively (for example, `amp auth pat --with-token`) as an alternative to `amp auth login`.

### Scopes

Request only the scopes an integration needs. The device-authorization request's `scope` field takes a space-delimited list; a PAT carries its own fixed set of scopes. `GET /v1/context` reports the active token's granted scopes in `principal.scopes`.

| Scope | Grants |
| --- | --- |
| `projects:read` | List projects and read the authenticated context. |
| `taxonomy:read` | Read event, event-property, and user-property taxonomy. |
| `taxonomy:write` | Create, update, and delete taxonomy entries. |
| `analytics:read` | List, get, and query saved charts. |
| `flags:read` | Read feature flag configuration. |
| `flags:write` | Create, update, and archive feature flags. |
| `destinations:read` | List and read event-streaming destinations. |
| `destinations:write` | Create and update event-streaming destinations. |

A request whose token lacks a required scope returns a `403` error. For the shared error format, refer to [Conventions](https://amplitude.com/docs/apis/developer#conventions).

## Conventions

### Pagination

List endpoints accept a `cursor` query parameter (an opaque value from the previous response's `pagination.next_cursor`) and a `limit` query parameter (default 50, max 200). Each list response includes a `pagination` object:

```json
{
  "pagination": {
    "next_cursor": "eyJvZmZzZXQiOjUwfQ",
    "has_more": true
  }
}
```

`next_cursor` is `null` on the last page. The underlying search index caps results at 10,000 per query. Well-formed pagination stops before that cap; a request whose cursor would exceed it returns a `400` error with `error_code: pagination_not_supported`.

### Errors

Error responses use [RFC 9457](https://www.rfc-editor.org/rfc/rfc9457) problem details, served as `application/problem+json`:

```json
{
  "type": "https://developer-api.amplitude.com/errors/validation",
  "title": "Validation failed",
  "status": 400,
  "detail": "limit must be less than or equal to 200",
  "instance": "/v1/projects/abc123/charts",
  "error_code": "validation_error",
  "retryable": false,
  "retry_after_seconds": null,
  "validation_errors": null
}
```

`retryable` tells you whether retrying the same request could succeed; when `retryable` is `true`, `retry_after_seconds` may suggest a backoff. A request-validation failure populates `validation_errors`.

### Idempotency

Requests that accept an `Idempotency-Key` require a unique string between 8 and 255 characters. Reuse the same key to safely retry the request without risking a duplicate mutation. Check each endpoint's parameters to determine whether it accepts this header.

### Dry runs

Delete and archive operations accept a `dry_run` query parameter (default `false`). Set `dry_run=true` to validate a request against the same checks the live operation runs, without applying the change.

