Messages format

POST /v1/messages: the same models in the Anthropic SDK format.

On this page

The API speaks the OpenAI and Anthropic SDK formats. This page is the Anthropic one: POST https://api.dipoleml.com/v1/messages. Keys, credits, prices and limits are the same as for Chat Completions.

Make a request

curl

curl https://api.dipoleml.com/v1/messages \  -H "x-api-key: $RAVEN_API_KEY" \  -H "anthropic-version: 2023-06-01" \  -H "Content-Type: application/json" \  -d '{    "model": "Raven Max",    "max_tokens": 1024,    "system": "You are a concise technical editor.",    "messages": [{"role": "user", "content": "Tighten this paragraph: ..."}]  }'

python

import osfrom anthropic import Anthropicclient = Anthropic(    base_url="https://api.dipoleml.com",    api_key=os.environ["RAVEN_API_KEY"],)message = client.messages.create(    model="Raven Max",    max_tokens=1024,    system="You are a concise technical editor.",    messages=[{"role": "user", "content": "Tighten this paragraph: ..."}],)print(message.content[0].text)

The SDK's base URL is https://api.dipoleml.com, without /v1: the SDK adds the path itself. The key goes in x-api-key, or as Authorization: Bearer (the SDK's auth_token).

response
{  "id": "msg_<request id>",  "type": "message",  "role": "assistant",  "model": "Raven Max",  "content": [{"type": "text", "text": "..."}],  "stop_reason": "end_turn",  "stop_sequence": null,  "usage": {    "input_tokens": 412,    "cache_creation_input_tokens": 0,    "cache_read_input_tokens": 0,    "output_tokens": 96  }}
  • id is msg_ followed by the request id, which is also in the request-id and x-request-id headers.
  • input_tokens leaves out cached input, which is counted in cache_read_input_tokens and billed at the cached price. cache_creation_input_tokens is always 0: caching needs no setup, and cache_control marks are accepted and not needed.
  • stop_reason is end_turn, max_tokens, stop_sequence (with the sequence in stop_sequence), tool_use or refusal.

Request fields

FieldNotes
modelRequired. Raven Flash or Raven Max, in any letter case (Models).
messagesRequired, not empty. Roles user and assistant; content is a string or a list of blocks.
max_tokensA positive whole number: the most tokens the answer may use, reasoning included.
systemA string or a list of text blocks.
temperature, top_p, top_kSampling fields.
stop_sequencesA list of strings.
streamtrue for an event stream (below).
tools, tool_choiceYour own tools (Tool calling).
thinking, output_config.effortReasoning (below).

metadata is accepted and not used. A system message inside messages is accepted too: before the first turn it joins the system prompt; later, it is kept in place as a note to the model.

Content blocks

  • text.
  • image, whose source is {"type": "base64", "media_type": "image/png", "data": "..."} or {"type": "url", "url": "https://..."} (Vision).
  • tool_use in assistant turns and tool_result in user turns, with is_error when your tool failed.
  • thinking blocks from earlier answers may be sent back. Only those of the current tool loop are passed to the model.

A block type the API does not take is refused with 400 and a message that names it.

Thinking and effort

output_config.effort sets how much the model reasons: low and medium give a low level, high (or leaving it out) the default level, xhigh and max the highest. You may also send the OpenAI-format field reasoning_effort (Reasoning); it takes the lead when both are sent.

The model's reasoning comes back only when you ask for it with "thinking": {"type": "enabled"} or {"type": "adaptive"}, and not with "display": "omitted". It is then the first block of the answer:

content (with thinking)
[  {"type": "thinking", "thinking": "The user wants...", "signature": "rvn_<request id>"},  {"type": "text", "text": "..."}]

The signature is an opaque string. Send the block back unchanged in a tool loop. budget_tokens is accepted; max_tokens is what bounds the answer.

Streaming

With "stream": true the answer is an event stream in the Anthropic SDK's order: message_start, ping, then for each block content_block_start, its content_block_delta events and content_block_stop, then message_delta with the stop_reason and the final usage, and message_stop.

event stream (abridged)
event: message_startdata: {"type":"message_start","message":{"id":"msg_<request id>","type":"message","role":"assistant","model":"Raven Max","content":[],"stop_reason":null,"stop_sequence":null,"usage":{"input_tokens":410,"cache_creation_input_tokens":0,"cache_read_input_tokens":0,"output_tokens":0}}}event: pingdata: {"type":"ping"}event: content_block_startdata: {"type":"content_block_start","index":0,"content_block":{"type":"text","text":""}}event: content_block_deltadata: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"Caching keeps"}}event: content_block_stopdata: {"type":"content_block_stop","index":0}event: message_deltadata: {"type":"message_delta","delta":{"stop_reason":"end_turn","stop_sequence":null},"usage":{"input_tokens":412,"cache_creation_input_tokens":0,"cache_read_input_tokens":0,"output_tokens":96}}event: message_stopdata: {"type":"message_stop"}
  • The input_tokens in message_start is an estimate; the count in message_delta is the one that is billed.
  • Deltas are text_delta, thinking_delta and signature_delta (for a thinking block), and input_json_delta (for a tool call).
  • While the model reasons and you did not ask to see it, ping events keep the connection open.
  • If the answer stops before it finishes, the stream ends with an error event of type api_error and no message_stop.
python
with client.messages.stream(    model="Raven Flash",    max_tokens=1024,    messages=[{"role": "user", "content": "Explain caching in three sentences"}],) as stream:    for text in stream.text_stream:        print(text, end="", flush=True)

Counting tokens

POST /v1/messages/count_tokens takes the same body and answers {"input_tokens": ...}. It is a rough estimate, made without calling the model; it needs a valid key and is not billed.

Errors

Errors have the Anthropic SDK's shape:

401 response
{  "type": "error",  "error": {    "type": "authentication_error",    "message": "Missing API key. Send it in the 'x-api-key' header or as 'Authorization: Bearer <your key>'. Create one at https://dashboard.dipoleml.com/keys",    "code": "missing_api_key"  }}

A bad key is refused before anything else about the request is checked. Errors and limits lists every status, type and code.