Contents

API reference

Introduction

DMM AI Router provides a REST API that lets you call many LLMs with one API key and one request format. Requests are sent as JSON and responses come back as JSON, or as Server-Sent Events when streaming. Two endpoints are offered: GET /v1/models lists the models you can call and POST /v1/chat/completions generates a chat response. Embeddings, the Responses API and image or audio input and output are not offered.

When a model is available through several providers, the cheapest route is tried first. If a provider is temporarily unavailable, whether because of an outage, rate limiting or load, the next route takes over. The provider field in the response tells you which provider answered.

Provider fallbackYour app"provider": "b"DMM AI Routertries the cheapest route first1. first routeProvider A (cheapest)temporarily unavailable2. next routeProvider Banswered
Base URL
https://api.airouter.dmm.com/v1
curl https://api.airouter.dmm.com/v1/chat/completions \
  -H "Authorization: Bearer $AIROUTER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model": "google/gemini-2.5-flash", "messages": [{"role": "user", "content": "Hello!"}]}'

Authentication

Pass your API key in the Authorization: Bearer header. Keys are issued under "API keys" in the dashboard, and usage is settled from the balance of the workspace the key belongs to. You can create several keys for different purposes. A revoked key stops working immediately and cannot be restored.

Each key can have a monthly spending limit. Once it is reached, requests with that key are rejected with 402 (monthly_limit_exceeded). Usage resets on the 1st of each month at 00:00 UTC.

  • Use keys only on the server. Never embed them in a browser or mobile app.
  • Keep keys out of source code. Load them from environment variables or a secret manager.
HTTP
POST /v1/chat/completions HTTP/1.1
Host: api.airouter.dmm.com
Authorization: Bearer sk-air-...
Content-Type: application/json
Shell
curl https://api.airouter.dmm.com/v1/models \
  -H "Authorization: Bearer $AIROUTER_API_KEY"

Models

GET/v1/models

Lists the models you can call right now. id is the value to pass as model, and owned_by is the model developer.

Context length, maximum output, price and data retention policy for each model are on the models page. The listed price is for the cheapest route; usage is charged at the price of the route that answered.

Returns

A list of model objects.

The list object

  • objectstring

    Always list

    Possible values:list
  • dataarray

    The models

    Show 4 child attributes
    • idstring

      Public model ID. The value to pass as model

    • objectstring

      Always model

      Possible values:model
    • createdinteger

      Registration time in UNIX seconds

    • owned_bystring

      The model developer

Request
curl https://api.airouter.dmm.com/v1/models \
  -H "Authorization: Bearer $AIROUTER_API_KEY"
ResponseJSON
{
  "object": "list",
  "data": [
    {
      "id": "google/gemini-2.5-flash",
      "object": "model",
      "created": 1759363200,
      "owned_by": "google"
    },
    {
      "id": "deepseek/deepseek-v4-pro",
      "object": "model",
      "created": 1759363200,
      "owned_by": "deepseek"
    }
  ]
}

Chat Completions

POST/v1/chat/completions

Generates a model reply for a conversation. With stream set to true, returns a sequence of chat.completion.chunk objects over Server-Sent Events.

Generation parameters such as temperature and reasoning_effort are applied on a best-effort basis, depending on the provider and model.

Request body

  • modelstringrequired

    Public model ID. Same as id from GET /v1/models

  • messagesarrayrequired

    The conversation, oldest first, with at least one entry. Up to 2,000,000 characters of JSON

    Show 6 child attributes
    • rolestringrequired

      Who is speaking

      Possible values:systemuserassistanttool
    • contentstring | array | nullrequired

      The text, as a string or an array of parts with type set to text. May be null for an assistant message that only called tools

    • namestring

      An identifier for the speaker

      Applies to: system, user, assistant

    • tool_callsarray

      The tool calls the model returned, passed back as is

      Applies to: assistant

      Show 3 child attributes
      • idstringrequired

        Call ID. Matches tool_call_id on the tool message

      • typestringrequired

        Always function

        Possible values:function
      • functionobjectrequired

        The function to call

        Show 2 child attributes
        • namestringrequired

          Function name

        • argumentsstringrequired

          Arguments as a JSON string, exactly as the model produced them

    • refusalstring | null

      The refusal text when the model declined to answer

      Applies to: assistant

    • tool_call_idstringrequired

      The id of the tool call this message answers

      Applies to: tool

  • streambooleanDefault: false

    true to receive the reply incrementally over Server-Sent Events

  • stream_optionsobject

    Streaming options. Allowed only when stream is true

    Show 1 child attributes
    • include_usageboolean

      Has no effect; the usage event is always sent

  • max_completion_tokensinteger

    Maximum number of output tokens

  • max_tokensinteger

    Legacy name for max_completion_tokens. Specify only one of the two

  • reasoning_effortstring

    Reasoning effort. Accepted values and their effect depend on the model

  • temperaturenumber

    Sampling randomness, from 0 to 2

  • top_pnumber

    Nucleus sampling probability mass, from 0 to 1

  • seedinteger

    Random seed for deterministic output. Guarantees depend on the model

  • presence_penaltynumber

    Penalty for tokens already present, from -2 to 2

  • frequency_penaltynumber

    Penalty proportional to token frequency, from -2 to 2

  • response_formatobject

    Output format. No constraint when omitted

    Show 2 child attributes
    • typestringrequired

      The kind of format

      Possible values:textjson_objectjson_schema
    • json_schemaobject

      Required when type is json_schema

      Show 4 child attributes
      • namestringrequired

        Schema name: letters, digits, _ and -, 1 to 64 characters

      • descriptionstring

        What the schema describes

      • schemaobjectrequired

        A JSON Schema object

      • strictboolean

        true to require strict adherence to the schema

  • toolsarray

    Functions the model may call

    Show 2 child attributes
    • typestringrequired

      Always function

      Possible values:function
    • functionobjectrequired

      The function definition

      Show 4 child attributes
      • namestringrequired

        Function name

      • descriptionstring

        What the function does. Helps the model decide when to call it

      • parametersobject

        JSON Schema for the arguments

      • strictboolean

        true to require strict adherence to the parameter schema

  • tool_choicestring | objectDefault: auto

    Controls tool use: the string none, auto or required, or an object naming a function (type set to function and the name in function.name)

  • parallel_tool_callsboolean

    false asks for at most one tool call per reply

Limits

  • The request body may be up to 10 MiB. Larger bodies return 413 (payload_too_large).
  • messages as a whole may be up to 2,000,000 characters of JSON. Larger conversations return 400.

Returns

A chat.completion object. id equals the x-request-id response header.

The charge is prompt_tokens and completion_tokens multiplied by the model price, settled from the workspace balance. Consumption tax is added for residents of Japan.

The response object

  • idstring

    Request ID. Equals the x-request-id response header

  • objectstring

    Always chat.completion

    Possible values:chat.completion
  • createdinteger

    Creation time in UNIX seconds

  • modelstring

    The public model ID that answered

  • providerstring

    The ID of the provider that answered

  • choicesarray

    The generated reply. Always one entry

    Show 3 child attributes
    • indexinteger

      Choice index. Always 0

    • messageobject

      The model reply

      Show 4 child attributes
      • rolestring

        Always assistant

        Possible values:assistant
      • contentstring | null

        The reply text. null when the reply is tool calls only

      • refusalstring | null

        The refusal text when the model declined. Usually null

      • tool_callsarray

        Tool calls requested by the model. Omitted when there are none

        Show 3 child attributes
        • idstringrequired

          Call ID. Matches tool_call_id on the tool message

        • typestringrequired

          Always function

          Possible values:function
        • functionobjectrequired

          The function to call

          Show 2 child attributes
          • namestringrequired

            Function name

          • argumentsstringrequired

            Arguments as a JSON string, exactly as the model produced them

    • finish_reasonstring

      Why generation stopped

      Possible values:stoplengthtool_callscontent_filter
  • usageobject

    Token usage

    Show 5 child attributes
    • prompt_tokensinteger

      Input tokens

    • completion_tokensinteger

      Output tokens

    • total_tokensinteger

      Total

    • prompt_tokens_detailsobject

      Input breakdown. Present only when the provider returns it

    • completion_tokens_detailsobject

      Output breakdown. Present only when the provider returns it

Request
curl https://api.airouter.dmm.com/v1/chat/completions \
  -H "Authorization: Bearer $AIROUTER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "google/gemini-2.5-flash",
    "messages": [
      {"role": "system", "content": "You are a helpful assistant."},
      {"role": "user", "content": "Hello!"}
    ]
  }'
ResponseJSON
{
  "id": "01K72W3M9Q4ZJ8N2T6VY5XH0RB",
  "object": "chat.completion",
  "created": 1759968000,
  "model": "google/gemini-2.5-flash",
  "provider": "edensoft",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "Hello! How can I help you today?",
        "refusal": null
      },
      "finish_reason": "stop"
    }
  ],
  "usage": {
    "prompt_tokens": 17,
    "completion_tokens": 9,
    "total_tokens": 26
  }
}

Streaming

Set stream to true to receive text/event-stream. Each event is JSON after data: .

Event order

  1. Chunks carrying the content piece by piece. Each has a delta in choices[0].delta and arrives as generation proceeds
  2. The final chunk. usage holds the token counts
  3. data: [DONE], the end of the stream

Each chunk carries only the difference in choices[0].delta. Concatenate content in order to get the text. For tool calls, join fragments that share tool_calls[].index, and parse function.arguments as JSON only after concatenating it.

The chunk object

  • idstring

    Request ID. Equals the x-request-id response header

  • objectstring

    Always chat.completion.chunk

    Possible values:chat.completion.chunk
  • createdinteger

    Creation time in UNIX seconds

  • modelstring

    The public model ID that answered

  • providerstring

    The ID of the provider that answered

  • choicesarray

    The delta. The usage and error events carry a single empty choice

    Show 3 child attributes
    • indexinteger

      Choice index. Always 0

    • deltaobject

      What changed since the previous chunk

      Show 4 child attributes
      • rolestring

        assistant, on the first chunk only

      • contentstring | null

        A fragment of the reply text

      • refusalstring | null

        A fragment of the refusal text

      • tool_callsarray

        Tool call fragments. Join fragments with the same index

        Show 4 child attributes
        • indexintegerrequired

          Which call the fragment belongs to

        • idstring

          On the first fragment only

        • typestring

          function, on the first fragment only

        • functionobject

          Function fragment

          Show 2 child attributes
          • namestring

            On the first fragment only

          • argumentsstring

            A fragment of the arguments JSON string

    • finish_reasonstring | null

      null until the last content chunk. error when a failure happened after the stream started

      Possible values:stoplengthtool_callscontent_filtererror
  • usageobject

    Only on the usage event just before the end

    Show 5 child attributes
    • prompt_tokensinteger

      Input tokens

    • completion_tokensinteger

      Output tokens

    • total_tokensinteger

      Total

    • prompt_tokens_detailsobject

      Input breakdown. Present only when the provider returns it

    • completion_tokens_detailsobject

      Output breakdown. Present only when the provider returns it

  • errorobject

    Only on the final event after a mid-stream failure. Same shape as error in an error response

    Show 3 child attributes
    • messagestring

      Description of the error

    • codestring

      Stable identifier to branch on

    • paramstring | null

      Dotted path to the field at fault (array elements carry an index). null otherwise

Request
curl https://api.airouter.dmm.com/v1/chat/completions \
  -H "Authorization: Bearer $AIROUTER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "google/gemini-2.5-flash",
    "messages": [{"role": "user", "content": "Hello!"}],
    "stream": true
  }'
StreamSSE
data: {"id":"01K72W3M9Q4ZJ8N2T6VY5XH0RB","object":"chat.completion.chunk","created":1759968000,"model":"google/gemini-2.5-flash","provider":"edensoft","choices":[{"index":0,"delta":{"role":"assistant","content":""},"finish_reason":null}]}

data: {"id":"01K72W3M9Q4ZJ8N2T6VY5XH0RB","object":"chat.completion.chunk","created":1759968000,"model":"google/gemini-2.5-flash","provider":"edensoft","choices":[{"index":0,"delta":{"content":"Hello"},"finish_reason":null}]}

data: {"id":"01K72W3M9Q4ZJ8N2T6VY5XH0RB","object":"chat.completion.chunk","created":1759968000,"model":"google/gemini-2.5-flash","provider":"edensoft","choices":[{"index":0,"delta":{"content":"! How can I help you today?"},"finish_reason":"stop"}]}

data: {"id":"01K72W3M9Q4ZJ8N2T6VY5XH0RB","object":"chat.completion.chunk","created":1759968000,"model":"google/gemini-2.5-flash","provider":"edensoft","choices":[{"index":0,"delta":{},"finish_reason":null}],"usage":{"prompt_tokens":17,"completion_tokens":9,"total_tokens":26}}

data: [DONE]
The chunk objectJSON
{
  "id": "01K72W3M9Q4ZJ8N2T6VY5XH0RB",
  "object": "chat.completion.chunk",
  "created": 1759968000,
  "model": "google/gemini-2.5-flash",
  "provider": "edensoft",
  "choices": [
    {
      "index": 0,
      "delta": {"content": "Hello"},
      "finish_reason": null
    }
  ]
}

Tool calling

Give the model function definitions, receive call instructions, and send back the results.

  1. Send a request with tools describing each function: its name, description and a JSON Schema for its arguments.
  2. When the model decides to call a function, finish_reason is tool_calls and message.tool_calls lists the functions to call with their arguments as JSON strings.
  3. Run the function in your code, then append a message with role tool and tool_call_id set to the call id, right after the assistant message that contains tool_calls, and send the request again.
  4. The model returns a final reply that takes the results into account.
Request
curl https://api.airouter.dmm.com/v1/chat/completions \
  -H "Authorization: Bearer $AIROUTER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "google/gemini-2.5-flash",
    "messages": [{"role": "user", "content": "What is the weather in Tokyo?"}],
    "tools": [{
      "type": "function",
      "function": {
        "name": "get_weather",
        "description": "Get the current weather for a city.",
        "parameters": {
          "type": "object",
          "properties": {
            "city": {"type": "string", "description": "City name"}
          },
          "required": ["city"]
        }
      }
    }],
    "tool_choice": "auto"
  }'
ResponseJSON
{
  "id": "01K72W3M9Q4ZJ8N2T6VY5XH0RB",
  "object": "chat.completion",
  "created": 1759968000,
  "model": "google/gemini-2.5-flash",
  "provider": "edensoft",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": null,
        "refusal": null,
        "tool_calls": [
          {
            "id": "call_7Yq3",
            "type": "function",
            "function": {
              "name": "get_weather",
              "arguments": "{\"city\": \"Tokyo\"}"
            }
          }
        ]
      },
      "finish_reason": "tool_calls"
    }
  ],
  "usage": {
    "prompt_tokens": 58,
    "completion_tokens": 14,
    "total_tokens": 72
  }
}
Sending the result back
curl https://api.airouter.dmm.com/v1/chat/completions \
  -H "Authorization: Bearer $AIROUTER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "google/gemini-2.5-flash",
    "messages": [
      {"role": "user", "content": "What is the weather in Tokyo?"},
      {
        "role": "assistant",
        "content": null,
        "tool_calls": [
          {
            "id": "call_7Yq3",
            "type": "function",
            "function": {"name": "get_weather", "arguments": "{\"city\": \"Tokyo\"}"}
          }
        ]
      },
      {
        "role": "tool",
        "tool_call_id": "call_7Yq3",
        "content": "{\"temperature_c\": 24, \"condition\": \"sunny\"}"
      }
    ],
    "tools": [{
      "type": "function",
      "function": {
        "name": "get_weather",
        "description": "Get the current weather for a city.",
        "parameters": {
          "type": "object",
          "properties": {
            "city": {"type": "string", "description": "City name"}
          },
          "required": ["city"]
        }
      }
    }]
  }'
Receiving calls while streamingPython
calls = {}
for chunk in stream:
    choice = chunk.choices[0]
    for part in choice.delta.tool_calls or []:
        call = calls.setdefault(part.index, {"id": None, "name": None, "arguments": ""})
        if part.id:
            call["id"] = part.id
        if part.function and part.function.name:
            call["name"] = part.function.name
        if part.function and part.function.arguments:
            call["arguments"] += part.function.arguments
    if choice.finish_reason == "tool_calls":
        break
# calls[index]["arguments"] is now a complete JSON string

Structured outputs

Use response_format to constrain the reply to JSON. json_schema asks for JSON that follows a schema, and json_object asks for any valid JSON.

json_schema.name and json_schema.schema are required. With strict set to true, supporting models adhere strictly to the schema. message.content in the response is a JSON string, so parse it on your side.

Request
curl https://api.airouter.dmm.com/v1/chat/completions \
  -H "Authorization: Bearer $AIROUTER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "google/gemini-2.5-flash",
    "messages": [
      {"role": "system", "content": "Extract the event details."},
      {"role": "user", "content": "Team lunch on Friday at 12:30 in Shibuya."}
    ],
    "response_format": {
      "type": "json_schema",
      "json_schema": {
        "name": "event",
        "schema": {
          "type": "object",
          "properties": {
            "title": {"type": "string"},
            "day": {"type": "string"},
            "time": {"type": "string"},
            "location": {"type": "string"}
          },
          "required": ["title", "day", "time", "location"],
          "additionalProperties": false
        },
        "strict": true
      }
    }
  }'
ResponseJSON
{
  "role": "assistant",
  "content": "{\"title\":\"Team lunch\",\"day\":\"Friday\",\"time\":\"12:30\",\"location\":\"Shibuya\"}",
  "refusal": null
}
json_object exampleJSON
{
  "model": "google/gemini-2.5-flash",
  "messages": [
    {"role": "system", "content": "Answer in JSON with keys \"answer\" and \"confidence\"."},
    {"role": "user", "content": "What is the capital of Japan?"}
  ],
  "response_format": {"type": "json_object"}
}
Unsupported model responseJSON
{
  "error": {
    "message": "This model does not support JSON output.",
    "code": "unsupported_parameter",
    "param": "response_format"
  }
}

Errors

Errors are returned as an HTTP status with a JSON body: a single error object with message, code and param.

The error object

  • messagestring

    Description of the error

  • codestring

    Stable identifier to branch on

  • paramstring | null

    Dotted path to the field at fault (array elements carry an index). null otherwise

Errors mid-stream

Failures before the stream starts are ordinary HTTP error responses. After it starts, the HTTP status stays 200: an event with finish_reason error and an error object is sent, followed by [DONE]. Discard any partial text or retry.

HTTP status

HTTP statusMeaning
400The request is invalid
401The API key is invalid
402Insufficient balance, or the key monthly limit
404The model does not exist
413The body is too large
5xxServer or provider failure

Error codes

codeMeaning
validation_failed400The body does not match the specification. param points to the fieldFix the field named by param and message
invalid_request400The HTTP request itself is malformed, for example unparsable JSONCheck how the request is built
unsupported_parameter400The model does not support the requested option (such as JSON output)Remove the option or use another model
provider_rejected_request400Every provider rejected the request because of its content. A parameter or the input does not suit the modelDrop parameters, revise the input, or use another model
invalid_api_key401The key is missing, malformed or revokedCheck the Authorization header and the key
insufficient_credits402The workspace balance is too lowTop up
monthly_limit_exceeded402The key reached its monthly limitRaise the limit or wait for next month
model_not_found404The model does not exist or has been retiredCheck the ID with GET /v1/models
payload_too_large413The body exceeds 10 MiBReduce the input
internal_error5xxA server or provider failureRetry after a short wait

Requests that produce no response are not billed.

Every response carries an x-request-id header. When contacting support, include this value and the time of the request.

ResponseHTTP
HTTP/1.1 400 Bad Request
Content-Type: application/json; charset=utf-8
x-request-id: 01K72W3M9Q4ZJ8N2T6VY5XH0RB

{
  "error": {
    "message": "messages[1].role: role must be one of system, user, assistant or tool.",
    "code": "validation_failed",
    "param": "messages[1].role"
  }
}
Errors mid-streamSSE
data: {"id":"01K72W3M9Q4ZJ8N2T6VY5XH0RB","object":"chat.completion.chunk","created":1759968000,"model":"google/gemini-2.5-flash","provider":"edensoft","choices":[{"index":0,"delta":{"content":"Hello"},"finish_reason":null}]}

data: {"id":"01K72W3M9Q4ZJ8N2T6VY5XH0RB","object":"chat.completion.chunk","created":1759968000,"model":"google/gemini-2.5-flash","provider":"edensoft","choices":[{"index":0,"delta":{},"finish_reason":"error"}],"error":{"message":"An unexpected error occurred. Please try again later.","code":"internal_error","param":null}}

data: [DONE]