> ## Documentation Index
> Fetch the complete documentation index at: https://docs.marblecms.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Marble API Reference

> Overview of the Marble REST API: base URL, API key authentication, scope-based permissions, JSON response format, and HTTP error codes.

The Marble API is organized around REST. Our API has predictable resource-oriented URLs, accepts form-encoded request bodies, returns JSON-encoded responses, and uses standard HTTP response codes and verbs.

## Base URL

All API access is over HTTPS and is accessed from the `api.marblecms.com` domain. All data is sent and received as JSON.

```
https://api.marblecms.com/v1/:resource
```

Where `:resource` is the specific resource you want to interact with (e.g., `posts`, `categories`, `tags`, `authors`).

## Authentication

Marble authenticates API requests using API keys. You can create and manage API keys from your workspace dashboard under **Settings > API Keys**.

Include your API key in the `Authorization` header of every request:

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl -H "Authorization: YOUR_API_KEY" https://api.marblecms.com/v1/posts
```

Alternatively, you can pass the API key as a query parameter:

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl "https://api.marblecms.com/v1/posts?key=YOUR_API_KEY"
```

### API key types and scopes

Every API key carries a set of **scopes** that determine which resources and operations it can access. The API checks the key's scopes on every request and returns `403 Forbidden` if a required scope is missing.

There are two key types:

* **Public keys** (`mpk_…`) can only hold read scopes. They are safe to use from environments where the key may be observed, such as build pipelines for static sites.
* **Private keys** (`msk_…`) can hold any scope, including write scopes and `posts_read_drafts`. Use them only from trusted server-side environments.

The full list of scopes:

| Scope               | Key types       | Grants                                           |
| ------------------- | --------------- | ------------------------------------------------ |
| `posts_read`        | public, private | Read published posts                             |
| `posts_read_drafts` | private         | Read drafts via `?status=draft` or `?status=all` |
| `posts_write`       | private         | Create, update, and delete posts                 |
| `authors_read`      | public, private | Read authors                                     |
| `authors_write`     | private         | Create, update, and delete authors               |
| `categories_read`   | public, private | Read categories                                  |
| `categories_write`  | private         | Create, update, and delete categories            |
| `tags_read`         | public, private | Read tags                                        |
| `tags_write`        | private         | Create, update, and delete tags                  |
| `media_read`        | public, private | Read media assets                                |
| `media_write`       | private         | Upload, update, and delete media assets          |
| `fields_read`       | public, private | Read custom fields                               |
| `fields_write`      | private         | Create, update, and delete custom fields         |

<Warning>
  Public API keys are scoped to read operations only, but they should still be
  handled with care. Exposing your key in client-side code allows anyone to
  make requests on your behalf, which can lead to your [rate
  limits](/api/rate-limits) being exhausted. We recommend using keys on the
  server-side whenever possible.
</Warning>

<Tip>
  **Best Practice:** Use the `Authorization` header for all requests. Query
  parameters may be logged in server access logs.
</Tip>

### Reading drafts

By default, `GET /v1/posts` returns only published posts. To include drafts you must:

1. Use a **private** API key, and
2. Grant it the `posts_read_drafts` scope, and
3. Pass `?status=draft` (drafts only) or `?status=all` (drafts + published).

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl -H "Authorization: msk_…" \
  "https://api.marblecms.com/v1/posts?status=draft"
```

Requests for drafts using a public key, or a private key without `posts_read_drafts`, return `403 Forbidden`.

## Response Format

All responses are returned as JSON. Successful responses include the requested data, while errors include an `error` field with details.

<Note>All timestamps are in UTC ISO 8601 format: `YYYY-MM-DDTHH:MM:SSZ`</Note>

## Error Handling

The API uses conventional HTTP response codes to indicate the success or failure of a request:

| Code  | Description                                     |
| ----- | ----------------------------------------------- |
| `200` | Success                                         |
| `400` | Bad Request - Invalid parameters                |
| `401` | Unauthorized - Invalid or missing API key       |
| `403` | Forbidden - API key is missing a required scope |
| `404` | Not Found - Resource doesn't exist              |
| `429` | Too Many Requests - Rate limit exceeded         |
| `500` | Internal Server Error                           |

Example error response:

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "error": "Unauthorized",
  "message": "API key required. Provide via Authorization header or ?key= query parameter"
}
```

When the key is valid but lacks the scope required for the endpoint, the API returns `403` with a message naming the missing scope:

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "error": "Forbidden",
  "message": "API key missing required scope: posts_write"
}
```

To resolve this, edit the key in **Settings > API Keys** and grant the missing scope, or use a different key that already has it. Remember that public keys cannot be granted write scopes or `posts_read_drafts`.
