PortModels
Log in

Chat completions

The main endpoint — request shape, streaming, parameter passthrough, and cost reporting.

text
POST /openai/v1/chat/completions

OpenAI-compatible, so existing clients work by changing the base URL and the key.

Request#

bash
curl https://api.portmodels.com/openai/v1/chat/completions \
  -H "Authorization: Bearer $PORTMODELS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "deepseek-ai/DeepSeek-V3",
    "messages": [
      {"role": "system", "content": "You are concise."},
      {"role": "user", "content": "Summarize the Treaty of Westphalia in two sentences."}
    ]
  }'
FieldRequiredNotes
modelyesA model key from /v1/models
messagesyesList of {role, content} objects; both fields are strings
streamnotrue for server-sent events
session_idnoConversation identifier, 1–1,024 bytes; reuse it across turns and use a different value for each conversation
providernoWith provider_key, pins the request to an enabled provider route
provider_keynoEncrypted pmbyok:v1 envelope produced by a trusted client; never plaintext

Parameter passthrough#

You can send model parameters such as temperature, max_tokens, top_p, stop, response_format, tools, and reasoning-effort settings. Support depends on the selected model.

PortModels request fields include action, model, messages, stream, stream_options, session_id, routing, provider, provider_key, app_id, connected, and user.

For the browser setup flow, threat model, and billing details, see Bring your own provider key.

json
{
  "model": "deepseek-ai/DeepSeek-V3",
  "messages": [{"role": "user", "content": "..."}],
  "temperature": 0.2,
  "max_tokens": 800,
  "response_format": {"type": "json_object"}
}

Tip

If a request fails after you add an optional parameter, check that the selected model supports it. See Errors for retry and support guidance.

Response#

json
{
  "id": "chatcmpl-...",
  "object": "chat.completion",
  "created": 1771200000,
  "model": "deepseek-ai/DeepSeek-V3",
  "choices": [
    {
      "index": 0,
      "message": { "role": "assistant", "content": "..." },
      "finish_reason": "stop"
    }
  ],
  "usage": {
    "prompt_tokens": 42,
    "completion_tokens": 128,
    "total_tokens": 170,
    "cost": 0.00031
  }
}

usage.cost is a PortModels addition: what this request actually cost, in credits, already including the app markup where one applies. Standard OpenAI clients ignore the extra field.

Streaming#

Set "stream": true and read server-sent events:

bash
curl -N https://api.portmodels.com/openai/v1/chat/completions \
  -H "Authorization: Bearer $PORTMODELS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model": "deepseek-ai/DeepSeek-V3", "stream": true,
       "messages": [{"role": "user", "content": "Count to five."}]}'
text
data: {"choices":[{"delta":{"content":"One"}}]}

data: {"choices":[{"delta":{"content":", two"}}]}

data: {"usage":{"cost":0.00012}}

data: [DONE]

A cost event is emitted immediately before the [DONE] terminator, so a streaming client can report spend the same way a non-streaming one does.

Using an OpenAI client library#

python
from openai import OpenAI

client = OpenAI(
    api_key="pm_...",
    base_url="https://api.portmodels.com/openai/v1",
)

response = client.chat.completions.create(
    model="deepseek-ai/DeepSeek-V3",
    messages=[{"role": "user", "content": "Hello"}],
)
print(response.choices[0].message.content)

On behalf of a user#

Swap the API key for the user's Connect access token — nothing else changes:

bash
curl https://api.portmodels.com/openai/v1/chat/completions \
  -H "Authorization: Bearer pmc_..." \
  -H "Content-Type: application/json" \
  -d '{"model": "deepseek-ai/DeepSeek-V3", "messages": [{"role": "user", "content": "Hello"}]}'

The token must carry the models.run scope, the model must be in your app's allowed_models list, and the charge lands on the user's credits with your markup applied.