> This page is for Build.

> Append .md to any page URL for clean Markdown. Index: https://docs.speechify.ai/llms.txt.
>
> Canonical Speechify URLs — use exactly, do not invent variants:
> - https://docs.speechify.ai — this site (API reference, SDKs, quickstarts)
> - https://speechify.ai — marketing + product site
> - https://platform.speechify.ai — customer dashboard, signup, API keys, billing
> - https://api.speechify.ai — API base URL
> - https://github.com/Speechify-AI: GitHub org for the API (cookbook, demos, CLI). `github.com/speechify` does not exist.
> - https://status.speechify.ai — status + incidents
> - https://speechify.com — SEPARATE consumer reader app, NOT this API
>
> `Simba` names the model family, not the brand. Model ids: `simba-3.5` (48 languages under one id, English included; recommended), `simba-3.2` (English) and `simba-3.0` (English, German, Spanish, French, Italian and Portuguese; the default). `simba-english` and `simba-multilingual` are retired: a new workspace that sends either gets `400 model_retired`. `SimbaVoice` / `simbavoice.ai` are retired.
>
> Ask, don't scrape. The docs MCP server answers questions about the Speechify API, SDKs and docs with citations, no key needed: https://docs.speechify.ai/_mcp/server (Streamable HTTP, tool `searchDocs`). Setup: https://docs.speechify.ai/build/guides/get-started/connect-mcp

# List Projects

GET https://api.speechify.ai/v1/projects

List the workspace's projects, newest first. The implicit Default
project is not a row and is never listed; resources with no
`project_id` live in it. Archived projects are hidden unless
`include_archived=true`, and purged ones unless
`include_purged=true`. Cursor-paginated: omit `cursor` for the
first page; walk pages while `has_more` is true (default page size
50, max 200).


Reference: https://docs.sws.speechify.com/build/api-reference/v1/projects/get

## Authentication

- `Authorization` header (bearer token, required) — Enter your API key with the `Bearer` prefix, e.g. 'Bearer sk_...'.

## Request

### Query parameters

- `cursor` (string, optional) — Opaque pagination cursor from a previous response.
- `limit` (integer, optional, default: 50) — Max items per page (default 50, max 200).
- `include_archived` (boolean, optional, default: false) — Include archived projects. Defaults to `false`, so the list shows only live projects; archived ones stay readable by id.
- `include_purged` (boolean, optional, default: false) — Include purged projects that are still inside their 30-day restore window, each carrying the `purged_at` stamp its deadline is measured from. Defaults to `false`, so the list shows only projects that still exist. This is the only read that returns a purged project: the by-id read answers 404 for one, and a project past its window is never listed, because it is awaiting permanent deletion and a restore would refuse it. Independent of `include_archived`: a purge runs only from the archived state, so asking for purged projects never means asking for every archived one as well.

## Response

### 200

The workspace's projects.

- `next_cursor` (string, required, nullable) — Opaque keyset cursor for the next page. Pass back as the `cursor` request parameter. `null` when the caller has reached the end of the list (`has_more` is also `false` in that case).
- `has_more` (boolean, required) — True when more rows exist beyond this page.
- `projects` (list of Project, required)

## Errors

### 400 Bad Request Error

The request was malformed or failed validation. The response body is the standard `Error` envelope; for validation failures `error.fields` enumerates the offending fields as a `path -> message` map (code = `validation_failed`).

- `error` (ErrorDetail, required)
- `request_id` (string, optional) — Server-side request identifier. Echoes the `Speechify-Request-Id` response header. Stable across the request's lifetime, written to structured logs, and useful when reporting issues.

### 401 Unauthorized Error

Authentication is missing or invalid. The request did not carry a recognised credential (console session token, API key, or worker JWT).

- `error` (ErrorDetail, required)
- `request_id` (string, optional) — Server-side request identifier. Echoes the `Speechify-Request-Id` response header. Stable across the request's lifetime, written to structured logs, and useful when reporting issues.

### 429 Too Many Requests Error

Rate limit or concurrency limit exceeded. `error.code` says which ceiling, and they need different responses: `rate_limited` is the request-rate budget (slow down), `concurrency_limit_reached` is a workspace-wide concurrency ceiling (fewer at once, or raise it), and `conversation_turn_in_progress` is contention over one named conversation (keep one message in flight on it). Every 429 carries `Retry-After` and the request-rate budget headers; the active-call cap also carries `RateLimit-Remaining-Calls: 0`.

- `error` (ErrorDetail, required)
- `request_id` (string, optional) — Server-side request identifier. Echoes the `Speechify-Request-Id` response header. Stable across the request's lifetime, written to structured logs, and useful when reporting issues.

### 500 Internal Server Error

An unexpected server-side error occurred. Safe to retry with exponential backoff for idempotent requests.

- `error` (ErrorDetail, required)
- `request_id` (string, optional) — Server-side request identifier. Echoes the `Speechify-Request-Id` response header. Stable across the request's lifetime, written to structured logs, and useful when reporting issues.

## Types

### Project

A workspace project: a resource grouping that a member grant or a credential pin can also scope. Resources reference a project by id; a resource with no `project_id` lives in the implicit Default project. A project can carry member grants, which scope a granted member to the projects they hold, and the invoice stays workspace-level with no billing split. A project may carry a monthly spend LIMIT, which bounds work without splitting the bill, capacity CEILINGS such as `max_requests_per_minute` that sit below the workspace's own plan limits so one project cannot consume the whole allowance, and it may be ARCHIVED, which suspends all work and spend inside it while everything stays readable. An application building on this platform models one of its OWN business customers as a project: it is the level that both attributes cost (`monthly_spend`, grouped per project on the usage breakdown) and ENFORCES it (`monthly_budget` and the capacity ceilings). How many projects a workspace may hold is the workspace's own ceiling; a create past it answers `409 project_limit_reached`, and deleting an unused project frees a slot.

- `id` (string, required) — Workspace-scoped project identifier (prefixed external id).
- `name` (string, required) — Human-readable label, unique per workspace (case-insensitive).
- `resource_count` (integer, required) — How many resources this project holds. Records of work that happened are not counted, and never hold a project open. It is the count the unqualified delete refuses on, so a project reading `0` is one `DELETE /v1/projects/{project_id}` accepts with no body. That is what makes an application's own cleanup a single list call rather than a read per project.
- `created_at` (datetime, required)
- `updated_at` (datetime, required)
- `archived_at` (datetime, optional, nullable) — When the project was archived; `null` while it is live. While set, nothing new starts or bills inside the project and every such attempt answers `409 project_archived`. Absent rather than null while a console runs ahead of an API that predates archiving, which is why it is not required: read absent and null alike.
- `purged_at` (datetime, optional) — When this project was purged, present only on the rows returned by `include_purged=true`. A purged project is kept for 30 days from this stamp and permanently deleted after that, so the restore deadline is this value plus the window; it is absent on every project that still exists. Restore it with `POST /v1/projects/{project_id}/restore`.
- `max_requests_per_minute` (integer, optional) — The most API requests per minute credentials pinned to this project may make across every surface, present only when set. Checked after the workspace's own request-rate limit, in one bucket per project: a request over the ceiling is refused with the same `429 rate_limited` the workspace limit answers, while other projects and unpinned credentials are untouched. Never higher than the workspace's widest per-surface rate over a minute: a project can narrow the workspace's capacity, not raise it. Console sessions and unpinned keys carry no project and are never subject to it.
- `monthly_budget` (double, optional) — The project's monthly spend limit in US dollars, present only when one is set. New billable work attributed to this project is refused with the coded `402 project_spend_limit_exceeded` once `monthly_spend` reaches it; the limit resets at the calendar-month boundary (UTC). Spend is attributed the same way it is billed: work from a project-pinned API key counts against that key's project, and work by a resource in a project counts against that project. The implicit Default project cannot carry a limit, since it has no project record, so spend there is bounded by the workspace's `monthly_budget` instead.
- `monthly_spend` (double, optional) — The project's billed month-to-date spend in US dollars, present whenever the billing plane answered - regardless of whether a spend limit is set.
- `monthly_budget_status` (enum, optional) — Where `monthly_spend` stands against `monthly_budget`, present only when both are: `warning` from 80% of the budget, `reached` once the spend has reached it and new billable work attributed to the project is refused with `402 project_spend_limit_exceeded`, `ok` below 80%. These are the thresholds the `project.spend_budget.warning` and `project.spend_budget.reached` webhook events fire at, so polling this field and subscribing to the events give the same answer.
  - Allowed values: `ok`, `warning`, `reached`
- `monthly_budget_remaining` (double, optional) — `monthly_budget` minus `monthly_spend` in US dollars, present only when both are. Negative once the spend has passed the budget, by the amount it overshot: enforcement trails billed usage by a couple of minutes, so work already under way can carry a project past its budget.

### ErrorDetail

- `code` (enum, required) — Stable machine-readable error code. Additive only: codes are never renamed, only deprecated. SDKs may map each code to a typed exception class. Status-code semantics: 4xx codes describe caller-fixable issues; 5xx codes describe server-side failures and are safe to retry with backoff for idempotent requests.
  - Allowed values: `endpoint_moved`, `bad_request`, `validation_failed`, `unauthorized`, `payment_required`, `forbidden`, `not_found`, `method_not_allowed`, `conflict`, `idempotency_conflict`, `payload_too_large`, `unsupported_media_type`, `rate_limited`, `concurrency_limit_reached`, `invalid_api_version`, `internal_error`, `upstream_failure`, `service_unavailable`, `caller_not_found`, `contact_not_found`, `contact_identifier_not_found`, `contact_identifier_conflict`, `contact_resolver_not_found`, `credential_not_found`, `credential_in_use`, `agent_not_found`, `agent_in_use`, `agent_run_not_found`, `kb_not_found`, `kb_document_not_found`, `kb_folder_not_found`, `tool_not_found`, `tool_name_taken`, `channel_instance_not_found`, `team_not_found`, `trigger_not_found`, `store_not_found`, `store_document_not_found`, `hosted_api_not_found`, `api_route_not_found`, `consumer_key_not_found`, `skill_not_found`, `skill_version_not_found`, `file_not_found`, `file_path_taken`, `file_storage_limit_reached`, `store_limit_reached`, `store_document_limit_reached`, `store_bytes_limit_reached`, `store_not_configured`, `store_document_version_conflict`, `store_document_deleted`, `entitlement_override_exists`, `hosted_apis_not_in_plan`, `skills_not_in_plan`, `voice_agents_not_in_plan`, `skill_in_use`, `skill_tool_name_conflict`, `skill_limit_reached`, `agent_skill_limit_reached`, `hosted_api_slug_taken`, `api_route_conflict`, `mount_plan_changed`, `route_output_unavailable`, `route_run_timeout`, `route_run_failed`, `route_run_limit_reached`, `route_read_limit_reached`, `route_write_limit_reached`, `hosted_api_public_refused`, `route_tool_not_readable`, `route_tool_unavailable`, `route_upstream_rate_limited`, `route_upstream_error`, `hosted_mcp_not_enabled`, `hosted_api_busy`, `conversation_not_found`, `phone_number_not_found`, `sip_trunk_not_found`, `voice_not_found`, `audio_asset_not_found`, `builtin_not_found`, `batch_not_found`, `agent_test_not_found`, `workspace_not_found`, `invite_not_found`, `project_not_found`, `cross_project_reference`, `project_has_scoped_credentials`, `project_not_empty`, `project_limit_reached`, `agent_limit_reached`, `project_too_large_to_promote`, `call_not_found`, `message_not_found`, `thread_not_found`, `call_not_active`, `relay_displaces_agent`, `brain_not_found`, `brain_in_use`, `custom_model_not_found`, `custom_model_in_use`, `insufficient_scope`, `purchased_numbers_not_included`, `phone_number_quota_reached`, `batch_calls_not_included`, `voice_cloning_not_included`, `voice_cloning_unavailable_in_region`, `consent_challenge_not_found`, `consent_challenge_expired`, `consent_challenge_already_used`, `consent_phrase_mismatch`, `consent_speaker_mismatch`, `consent_recording_unusable`, `consent_verification_unavailable`, `consent_verification_required`, `watermark_audio_unusable`, `watermark_detection_unavailable`, `workspace_last_owner`, `workspace_last_workspace`, `account_deletion_blocked`, `workspace_free_limit`, `workspace_single_owner`, `invite_email_mismatch`, `invite_already_pending`, `service_account_limit_reached`, `service_accounts_not_in_plan`, `speech_marks_unsupported`, `pronunciations_unsupported`, `model_retired`, `too_many_voices`, `content_policy_violation`, `safety_identifier_blocked`, `topup_not_in_plan`, `credit_purchase_unpaid`, `credit_purchase_payment_in_progress`, `tool_config_shared`, `spend_cap_exceeded`, `spend_budget_exceeded`, `project_spend_limit_exceeded`, `project_archived`, `project_not_archived`, `project_not_purged`, `project_restore_window_expired`, `project_name_taken`, `funded_balance_required`, `agent_publish_gate_failed`, `agent_publish_gate_required`, `agent_publish_gate_unavailable`, `agent_publish_gate_tool_unreachable`, `text_channel_not_in_plan`, `channel_not_in_plan`, `text_turn_failed`, `conversation_turn_in_progress`, `conversation_closed`, `conversation_not_reachable`, `conversation_channel_bound`, `conversation_prompt_not_found`, `text_message_quota_exceeded`, `durable_runs_not_in_plan`, `tool_transport_unsupported`, `agent_config_too_large`, `agent_run_not_pending`, `agent_run_action_stale`, `share_link_not_found`, `share_link_exhausted`, `share_link_limit_reached`, `destination_not_allowed`, `international_dialing_not_enabled`, `number_not_sms_capable`, `verification_required`, `intended_use_required`
- `message` (string, required) — Human-readable explanation of this specific occurrence. Safe to surface in UI banners or pass to support. The wording can change between releases; clients should match on `code`, not on the message string.
- `fields` (map from string to string, optional) — Per-field validation errors as `path -> message`. Only present on 400 responses caused by request validation (typically code=`validation_failed`). Keys are field paths in dotted/bracket notation; values are short human explanations safe to inline-surface next to the offending form field.
- `details` (map from string to any, optional) — Structured, endpoint-specific context beyond the flat `fields` map. Present only on the few errors that carry it (e.g. the `used_by` referrer list on a credential delete-conflict); its shape depends on the error `code`. Clients that don't recognise a `details` shape can ignore it - the `code` + `message` contract is unchanged.
- `docs_url` (string, optional) — Link to the documentation that resolves this class of error, when a stable page exists. Rate and concurrency 429s link the API limits reference, which lists each plan's limits and how to raise them.

## Examples

**Response**

```json
{
  "next_cursor": "string",
  "has_more": true,
  "projects": [
    {
      "id": "proj_01arz3ndektsv4rrffq69g5fav",
      "name": "string",
      "resource_count": 0,
      "created_at": "2024-01-15T09:30:00Z",
      "updated_at": "2024-01-15T09:30:00Z",
      "archived_at": "2024-01-15T09:30:00Z",
      "purged_at": "2026-08-01T09:14:22Z",
      "max_requests_per_minute": 1,
      "monthly_budget": 1.1,
      "monthly_spend": 1.1,
      "monthly_budget_status": "warning",
      "monthly_budget_remaining": 2.5,
      "max_concurrent_calls": 1,
      "max_concurrent_runs": 1
    }
  ]
}
```