# Messages API

Build with [Muse Spark](https://dev.meta.ai/docs/models#muse-spark) through an Anthropic-compatible interface. The Messages API lets you run Muse Spark with the Anthropic SDK and Claude-oriented tools: swap the base URL and key and keep the rest of your code. Requests shaped for Anthropic's `/v1/messages` run on Meta Model API unchanged. The Messages API serves the Muse Spark text models; see [model availability](https://dev.meta.ai/docs/models#model-availability) for the full model-to-endpoint list.

## How it works

The Messages endpoint is a thin wire-format adapter over the same pipeline that powers the [Responses API](https://dev.meta.ai/docs/protocols/responses). We translate your Anthropic request into a Responses request, run it through shared auth, rate limiting, billing, safety, media resolution, and routing, then translate the result back. Only the request and response shape differs.

The adapter is stateless. It always runs with storage off, so there is no server-managed conversation and no `previous_response_id` equivalent. Replay history client-side by appending prior assistant turns to `messages`, exactly as you do against Anthropic.

Every request follows the Anthropic shape:

- **messages**: array of `user` and `assistant` turns, where `content` is a string or an array of [content blocks](https://dev.meta.ai/docs/protocols/messages#content-blocks).
- **system**: text blocks that set behavior instructions, the Anthropic equivalent of a `developer` message.
- **max\_tokens**: required upper bound on tokens generated.
- **thinking**: reasoning control for Muse Spark. See [Reasoning](https://dev.meta.ai/docs/protocols/messages#reasoning).

See [Request fields](https://dev.meta.ai/docs/protocols/messages#request-fields) for the full set.

| Setting       | Value                                                 |
| ------------- | ----------------------------------------------------- |
| **Base URL**  | `https://api.meta.ai`                                 |
| **Endpoints** | `POST /v1/messages`, `POST /v1/messages/count_tokens` |
| **Model**     | `muse-spark-1.3`                                      |
| **Auth**      | Bearer token (`MODEL_API_KEY`)                        |

## Basic usage

Send `POST /v1/messages` with `model`, `messages`, and the required `max_tokens`. Authenticate with your Model API key as a bearer token. Keep your existing Anthropic SDK setup and point it at Model API — pass the base URL and your key as a bearer token.

**Python (Anthropic SDK)**

```python
import os

from anthropic import Anthropic

client = Anthropic(
    base_url="https://api.meta.ai",
    auth_token=os.environ["MODEL_API_KEY"],
)

message = client.messages.create(
    model="muse-spark-1.3",
    max_tokens=1024,
    messages=[
        {
            "role": "user",
            "content": "What is the capital of France?",
        },
    ],
)

print(message.model_dump_json(indent=2))
```

**Python (requests)**

```python
import json
import os

import requests

response = requests.post(
    "https://api.meta.ai/v1/messages",
    headers={
        "Authorization": f"Bearer {os.environ['MODEL_API_KEY']}",
        "Content-Type": "application/json",
    },
    json={
        "model": "muse-spark-1.3",
        "max_tokens": 1024,
        "messages": [
            {
                "role": "user",
                "content": "What is the capital of France?",
            },
        ],
    },
)
response.raise_for_status()
print(json.dumps(response.json(), indent=2))
```

**curl**

```shell
curl -X POST "https://api.meta.ai/v1/messages" \
  -H "Authorization: Bearer $MODEL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "model": "muse-spark-1.3",
  "max_tokens": 1024,
  "messages": [
    {
      "role": "user",
      "content": "What is the capital of France?"
    }
  ]
}'
```

A successful call returns an Anthropic `message` object:

```json
{
  "id": "msg_abc123",
  "type": "message",
  "role": "assistant",
  "model": "muse-spark-1.3",
  "content": [
    {"type": "text", "text": "The capital of France is Paris."}
  ],
  "stop_reason": "end_turn",
  "stop_sequence": null,
  "usage": {
    "input_tokens": 14,
    "output_tokens": 8
  }
}
```

## Request fields

- **model** (required): model ID, such as `muse-spark-1.3`.
- **messages** (required): non-empty array. Roles are `user` and `assistant`; `content` is a string or an array of [content blocks](https://dev.meta.ai/docs/protocols/messages#content-blocks).
- **max\_tokens** (required): upper bound on tokens generated. Maps to `max_output_tokens` on the underlying Responses request.
- **system**: string or array of `text` blocks that set behavior instructions. System content accepts text blocks only; any other block type returns `HTTP 400`.
- **temperature**: enforced to Anthropic's `0`–`1` range. **top\_p**: passed through. Muse Spark runs best at the default `temperature=1.0`; leave sampling unset for most workloads, and set `temperature` or `top_p`, not both.
- **stream**: when `true`, the response streams as Anthropic SSE events. See [Response](https://dev.meta.ai/docs/protocols/messages#response).
- **metadata**: string values forwarded to Responses `metadata`. `metadata.user_id` also sets the [safety identifier](https://dev.meta.ai/docs/protocols/responses#safety-identifiers) for the request (the Anthropic-native equivalent of `safety_identifier`); values over 64 bytes are stably hashed to fit. Use a privacy-preserving value, such as a hash of an internal user ID.
- **service\_tier**: `auto` and `standard_only` accepted; other values return `HTTP 400`.
- **thinking** / **output\_config.effort**: reasoning control. See [Reasoning](https://dev.meta.ai/docs/protocols/messages#reasoning).
- **output\_config.format**: `{type: "json_schema", schema}` constrains output to a JSON schema. See [structured output](https://dev.meta.ai/docs/structured-output).
- **tools** / **tool\_choice**: see [Tools](https://dev.meta.ai/docs/protocols/messages#tools).

These top-level fields are not supported and return `HTTP 400`: `stop_sequences`, `top_k`, `container`, and `inference_geo`. Unknown top-level fields are also rejected.

## Content blocks

**User messages** accept:

- **text**: plain text input.
- **image**: source is `base64`, `url`, or `file` (an uploaded file ID). See [image understanding](https://dev.meta.ai/docs/image-understanding).
- **video**: source is `base64`, `url`, or `file`, with optional `fps`. See [video understanding](https://dev.meta.ai/docs/video-understanding).
- **document**: source is `base64`, `url`, `file`, `text`, or `content`. See [files](https://dev.meta.ai/docs/file-handling).
- **tool\_result**: result of a prior tool call, keyed by `tool_use_id`. Maps to `function_call_output` underneath.
- **search\_result**: prior search results forwarded as input text.

**Assistant messages** (when you replay prior turns) accept:

- **text**: prior text output.
- **tool\_use**: a function call the model made on a prior turn.
- **server\_tool\_use**: replay of a built-in call, such as `web_search` or `tool_search`, from a prior turn.
- **thinking** / **redacted\_thinking**: prior reasoning. `redacted_thinking` carries the encrypted blob; replay it to preserve chain of thought. See [reasoning](https://dev.meta.ai/docs/reasoning).

## Reasoning

Muse Spark always reasons. Control depth with `thinking` and optional `output_config.effort`:

- **thinking: {type: "adaptive"}**: reason at the default effort with a summarized output. Add `output_config.effort` to override depth.
- **output\_config.effort**: `low`, `medium`, `high`, and `xhigh` pass through.
- **thinking: {type: "enabled", budget\_tokens: n}**: accepted for compatibility but not translated into an effort value. Requires `budget_tokens >= 1024` and `< max_tokens`. Use `output_config.effort` for depth control.
- **thinking: {type: "disabled"}**: requests no reasoning. Muse Spark does not support disabling reasoning, so this returns `HTTP 400`.
- **display**: `summarized` (default) returns a thinking summary; `omitted` returns encrypted reasoning only, with no visible summary.

## Tools

The Messages endpoint supports developer-defined and built-in tools:

- **custom**: developer-defined function. Its `input_schema` becomes the function parameters, and it carries `description`, `strict`, and `defer_loading`. See [tool calling](https://dev.meta.ai/docs/tool-calling).
- **web\_search**: built-in web search for grounded, cited answers. You can pass `user_location`; `allowed_domains`, `blocked_domains`, and `max_uses` return `HTTP 400`. See [search grounding](https://dev.meta.ai/docs/search-grounding).
- **tool\_search**: built-in deferred-tool discovery. Mark deferrable tools with `defer_loading: true` to let the model load their schemas on demand. See [tool search](https://dev.meta.ai/docs/tool-search).

Map `tool_choice` as follows:

- **auto**: model decides whether to call a tool.
- **any**: requires a tool call.
- **none**: drops tools for this turn.
- **disable\_parallel\_tool\_use**: when `true`, prevents parallel tool calls in a single turn. A named tool choice (`{type: "tool"}`) is not supported and returns `HTTP 400`.

## Response

A response is an Anthropic `message` object:

- **content**: array of blocks. Model text maps to `text`; reasoning maps to `thinking` (`encrypted_content` maps to `redacted_thinking`); a function call maps to `tool_use`; a built-in call such as `web_search_call` or `tool_search_call` maps to `server_tool_use`.
- **stop\_reason**: `tool_use` when the model called a tool; `max_tokens` when it hit the output limit; `refusal` when it declined; otherwise `end_turn`.
- **usage**: `input_tokens`, `output_tokens`, `cache_read_input_tokens`, and `output_tokens_details.thinking_tokens` for reasoning tokens.

With `stream: true`, the response streams as the standard Anthropic event sequence: `message_start`, `content_block_start`, `content_block_delta`, `content_block_stop`, `message_delta`, and `message_stop`, with `Content-Type: text/event-stream`.

### Counting tokens

`POST /v1/messages/count_tokens` counts the input tokens an Anthropic-format request would consume without generating. Send the same body as `POST /v1/messages` minus the `max_tokens` requirement. The response is `{ "input_tokens": <integer> }`. See [token counting](https://dev.meta.ai/docs/token-counting).

## Error handling

Errors use the Anthropic envelope:

```json
{
  "type": "error",
  "error": {
    "type": "invalid_request_error",
    "message": "..."
  }
}
```

The `error.type` maps from the HTTP status: `400` is `invalid_request_error`, `401` is `authentication_error`, `403` is `permission_error`, `404` is `not_found_error`, `429` is `rate_limit_error`, `503` is `overloaded_error`, and other `5xx` responses are `api_error`.

## Next steps

- Ship with OpenAI-style requests when you prefer them: use the [Responses API](https://dev.meta.ai/docs/protocols/responses) or [Chat Completions](https://dev.meta.ai/docs/protocols/chat-completions) over the same models.
- Ground answers in live web data with [search grounding](https://dev.meta.ai/docs/search-grounding).
- Connect Muse Spark to your own code with [tool calling](https://dev.meta.ai/docs/tool-calling) and on-demand [tool search](https://dev.meta.ai/docs/tool-search).
- See the full request and response shape in the [Messages API reference](https://dev.meta.ai/docs/api-reference/messages/create-message).
