Messages
Create a message (Anthropic format)
Anthropic Messages-compatible endpoint: point the Anthropic SDK's base URL at https://api.router.africa and keep your code. Set stream: true to receive Anthropic-style server-sent events.
Authenticate with x-api-key: YOUR_KEY (what the Anthropic SDK sends) or Authorization: Bearer YOUR_KEY. This is the only endpoint that accepts x-api-key.
Errors use Anthropic's error format, not the envelope of the other endpoints: {"type": "error", "error": {"type": "...", "message": "...", "reason": "..."}}. error.type is Anthropic's type; error.reason is our specific cause (see the Errors page). An error before the first token is an ordinary HTTP error. An error after streaming has started cannot change the 200; it is sent as a final event: error event and the stream ends.
Not supported: a fallbacks array and models (fallback chains are configured by router.africa, not per request).
https://api.router.africa/v1/messagesAuthorizations
Use one of these.
x-api-keyapiKeyheaderrequiredAccepted on POST /v1/messages only, for Anthropic SDKs. Bearer also works there. Never put a key in the URL or the body.
Authorizationbearer (API key (sk-ra-...))headerrequiredSend your key as Authorization: Bearer YOUR_KEY. Never put a key in the URL or the body: the request is refused with key_not_in_header.
Headers
Idempotency-Keystringrouter.africa extension. Works exactly as on POST /v1/chat/completions: a repeat of a finished request with the same key and the same API key returns the first result (marked x-idempotent-replay: true, not charged again); a repeat while the first is still running returns 409 request_in_progress. Without this header an identical body is treated the same way.
Body application/json
modelstringrequiredA model id from GET /v1/models.
messagesobject[]required2 child fields
rolestringrequiredcontentstring | MessageContentBlock[]requiredText, or an array of content blocks.
1 child field
variant 1MessageContentBlock[]3 child fields
typestringrequiredtextstringcache_controlobjectmax_tokensintegerrequiredOutput cap. A value above the model's ceiling is silently lowered to the ceiling; it is not an error.
systemstring | MessageContentBlock[]System prompt, as text or an array of content blocks.
1 child field
variant 1MessageContentBlock[]3 child fields
typestringrequiredtextstringcache_controlobjectstreambooleandefault: falsetemperaturenumbertop_pnumbertop_kintegerstop_sequencesstring[]toolsobject[]tool_choiceobjectthinkingobjectExtended thinking settings, as in Anthropic's API.
context_managementobjectmetadataobjectResponse
200
The message, or a stream of events when stream is true.
Response headers: x-request-id, x-idempotent-replay. Include x-request-id when you contact support.
idstringrequiredtype"message"requiredrole"assistant"requiredmodelstringrequiredcontentMessageContentBlock[]required3 child fields
typestringrequiredtextstringcache_controlobjectstop_reasonstring | nullrequiredstop_sequencestring | nullusageMessageUsagerequired4 child fields
input_tokensintegerrequiredoutput_tokensintegerrequiredcache_creation_input_tokensinteger | nullcache_read_input_tokensinteger | nullStreaming
Server-sent events in Anthropic's format: each event has an event: line naming its type and a data: line with one JSON MessageStreamEvent (or, last, an event: error event carrying an AnthropicError). There is no [DONE] line: the stream ends after message_stop.
Event: MessageStreamEvent
typestringrequiredindexintegermessageMessage8 child fields
idstringrequiredtype"message"requiredrole"assistant"requiredmodelstringrequiredcontentMessageContentBlock[]required3 child fields
typestringrequiredtextstringcache_controlobjectstop_reasonstring | nullrequiredstop_sequencestring | nullusageMessageUsagerequired4 child fields
input_tokensintegerrequiredoutput_tokensintegerrequiredcache_creation_input_tokensinteger | nullcache_read_input_tokensinteger | nullcontent_blockMessageContentBlockA content block. type is for example text, image, tool_use, tool_result or thinking. cache_control may be set on a block.
3 child fields
typestringrequiredtextstringcache_controlobjectdeltaobjectusageMessageUsage4 child fields
input_tokensintegerrequiredoutput_tokensintegerrequiredcache_creation_input_tokensinteger | nullcache_read_input_tokensinteger | nullEvent: AnthropicError
type"error"requirederrorobjectrequired4 child fields
typestringrequiredAnthropic's error type, from the HTTP status.
messagestringrequiredSafe to show to a user.
reasonstringrequiredOur specific cause, the same values as metadata.reason in the standard error.
modelstringOn model_not_found, the model id that was sent.
event: message_start
data: {"type":"message_start","message":{"id":"msg_0192f0e0","type":"message","role":"assistant","model":"anthropic/claude-sonnet-4.5","content":[],"stop_reason":null,"stop_sequence":null,"usage":{"input_tokens":12,"output_tokens":1}}}
event: content_block_start
data: {"type":"content_block_start","index":0,"content_block":{"type":"text","text":""}}
event: content_block_delta
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"Habari!"}}
event: content_block_stop
data: {"type":"content_block_stop","index":0}
event: message_delta
data: {"type":"message_delta","delta":{"stop_reason":"end_turn","stop_sequence":null},"usage":{"output_tokens":3}}
event: message_stop
data: {"type":"message_stop"}
(an error after streaming has started instead ends the stream with:)
event: error
data: {"type":"error","error":{"type":"overloaded_error","message":"The model provider is overloaded. Retry shortly.","reason":"provider_overloaded"}}Errors
Errors share one body shape. Branch on error.metadata.error_type for retry logic and on error.metadata.reason for what to show your user. The full table is in Errors.
400
Same causes as the standard 400 response, in Anthropic's error format.
{
"type": "error",
"error": {
"type": "invalid_request_error",
"message": "Request body is invalid: `messages` is required.",
"reason": "invalid_body"
}
}401
Same causes as the standard 401 response, in Anthropic's error format.
{
"type": "error",
"error": {
"type": "authentication_error",
"message": "You didn't provide an API key. Send it as `x-api-key: YOUR_KEY` or `Authorization: Bearer YOUR_KEY`.",
"reason": "missing_api_key"
}
}402
Same causes as the standard 402 response, in Anthropic's error format.
{
"type": "error",
"error": {
"type": "billing_error",
"message": "This workspace doesn't have enough credits for this request. Add credits at https://app.router.africa.",
"reason": "insufficient_credits"
}
}403
Same causes as the standard 403 response, in Anthropic's error format.
{
"type": "error",
"error": {
"type": "permission_error",
"message": "This account is suspended. Contact support.",
"reason": "account_suspended"
}
}404
Same causes as the standard 404 response, in Anthropic's error format.
{
"type": "error",
"error": {
"type": "not_found_error",
"message": "The model `anthropic/unknown` does not exist or you do not have access to it.",
"reason": "model_not_found"
}
}408
Same causes as the standard 408 response, in Anthropic's error format.
{
"type": "error",
"error": {
"type": "api_error",
"message": "The model provider took too long to respond.",
"reason": "upstream_timeout"
}
}409
Same causes as the standard 409 response, in Anthropic's error format.
{
"type": "error",
"error": {
"type": "invalid_request_error",
"message": "An identical request is already in progress.",
"reason": "request_in_progress"
}
}413
Same causes as the standard 413 response, in Anthropic's error format.
{
"type": "error",
"error": {
"type": "invalid_request_error",
"message": "The request body is too large. Send less than 16 MiB.",
"reason": "payload_too_large"
}
}429
Same causes as the standard 429 response, in Anthropic's error format.
{
"type": "error",
"error": {
"type": "rate_limit_error",
"message": "Rate limit reached for this API key. Retry after 5 seconds.",
"reason": "rate_limited"
}
}500
Same causes as the standard 500 response, in Anthropic's error format.
{
"type": "error",
"error": {
"type": "api_error",
"message": "Something went wrong on our side. Reference: 0192f0e0-aaaa-7000-8000-000000000000.",
"reason": "internal_error"
}
}502
Same causes as the standard 502 response, in Anthropic's error format.
{
"type": "error",
"error": {
"type": "api_error",
"message": "The model provider returned an error.",
"reason": "provider_error"
}
}503
Same causes as the standard 503 response, in Anthropic's error format.
{
"type": "error",
"error": {
"type": "api_error",
"message": "The service is temporarily unavailable.",
"reason": "service_unavailable"
}
}529
Same causes as the standard 529 response, in Anthropic's error format.
{
"type": "error",
"error": {
"type": "overloaded_error",
"message": "The model provider is overloaded. Retry shortly.",
"reason": "provider_overloaded"
}
}