Quickstart — your first API call in 5 minutes
Sign up with a phone number, top up your toman wallet, create an sk-up key, and make your first model call with the official OpenAI SDK.
Updated: September 7, 2026
uttapen is a gateway that speaks the official OpenAI API. Code that talks to OpenAI today reaches more than 400 models after a one-line change (base_url), and each request is paid for out of your toman wallet. This page takes you from signup to your first model response.
1. Sign up with your phone number
Open the login page and enter your mobile number. We send you a one-time code by SMS — type it in and you are done. Signing up and signing in are the same flow: your account is created the first time a code is verified. Later you can set a password in settings so you don't have to wait for an SMS every time.
2. Top up your wallet
Go to Dashboard → Wallet, enter an amount in toman and pay through the Zibal gateway. The minimum top-up is 50,000 toman. Your balance is credited the moment you come back from the gateway, and an invoice is issued for every payment.
Free models (ids ending in :free) work with an empty wallet, but they come with a daily cap and a pool shared between all users. For anything serious, add a small amount of credit so you don't run into 402 mid-project.
3. Create an API key
In Dashboard → Keys, click "New key", give it a name (local-dev, for example) and create it. The key starts with sk-up- and is shown once — copy it right away and put it in your project's .env. If you lose it, revoke it and create a fresh one.
Every key can carry a monthly spending limit in toman, a list of models it is allowed to call, its own rate limit and an expiry date. See Authentication and API keys for the details.
4. Your first request
The base URL is always https://api.uttapen.ir/v1, and model ids follow the provider/model shape. The sample below uses openai/gpt-5-mini; any id from the model list works in its place.
curl https://api.uttapen.ir/v1/chat/completions \
-H "Authorization: Bearer sk-up-…" \
-H "Content-Type: application/json" \
-d '{
"model": "openai/gpt-5-mini",
"messages": [{"role": "user", "content": "Introduce yourself in one sentence."}],
"stream": true
}'from openai import OpenAI
client = OpenAI(
base_url="https://api.uttapen.ir/v1",
api_key="sk-up-…",
)
stream = client.chat.completions.create(
model="openai/gpt-5-mini",
messages=[{"role": "user", "content": "Introduce yourself in one sentence."}],
stream=True,
)
for chunk in stream:
print(chunk.choices[0].delta.content or "", end="", flush=True)import OpenAI from "openai";
const client = new OpenAI({
baseURL: "https://api.uttapen.ir/v1",
apiKey: "sk-up-…",
});
const stream = await client.chat.completions.create({
model: "openai/gpt-5-mini",
messages: [{ role: "user", content: "Introduce yourself in one sentence." }],
stream: true,
});
for await (const chunk of stream) {
process.stdout.write(chunk.choices[0]?.delta?.content ?? "");
}<?php
// composer require openai-php/client guzzlehttp/guzzle
$client = OpenAI::factory()
->withBaseUri('https://api.uttapen.ir/v1')
->withApiKey('sk-up-…')
->make();
$result = $client->chat()->create([
'model' => 'openai/gpt-5-mini',
'messages' => [['role' => 'user', 'content' => 'Introduce yourself in one sentence.']],
]);
echo $result->choices[0]->message->content;package main
import (
"context"
"fmt"
"github.com/openai/openai-go"
"github.com/openai/openai-go/option"
)
func main() {
client := openai.NewClient(
option.WithBaseURL("https://api.uttapen.ir/v1"),
option.WithAPIKey("sk-up-…"),
)
resp, err := client.Chat.Completions.New(context.Background(), openai.ChatCompletionNewParams{
Model: "openai/gpt-5-mini",
Messages: []openai.ChatCompletionMessageParamUnion{openai.UserMessage("Introduce yourself in one sentence.")},
})
if err != nil {
panic(err)
}
fmt.Println(resp.Choices[0].Message.Content)
}If the SDK isn't installed yet:
pip install openai # Python
npm install openai # Node.js
Never hardcode the key — read it from the environment:
import os
from openai import OpenAI
client = OpenAI(
base_url="https://api.uttapen.ir/v1",
api_key=os.environ["UTTAPEN_API_KEY"],
)
resp = client.chat.completions.create(
model="openai/gpt-5-mini",
messages=[{"role": "user", "content": "Give me three advantages of Postgres, one line each."}],
max_tokens=200,
)
print(resp.choices[0].message.content)
print(resp.usage.prompt_tokens, resp.usage.completion_tokens)
5. What comes back
The response body is exactly OpenAI's shape: id, model, choices[0].message.content, finish_reason and a usage block. You also get two extras:
usage— the input and output token counts the model reported for this specific request. That real usage is what you are billed for.- a few
X-Uttapen-headers on non-streaming responses:
X-Uttapen-Request-Id: 01a078dd-853e-7480-aa83-262d3239e6a2
X-Uttapen-Model: openai/gpt-5-mini
X-Uttapen-Cost-Toman: 6.659688
X-Uttapen-Balance-Toman: 97564.413809
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 59
Include X-Uttapen-Request-Id whenever you report a problem — the same id is recorded in our logs and in your ledger. In Python you reach these headers with client.chat.completions.with_raw_response.create(...), in Node with .withResponse().
The toman amount for a request is derived from the usage the model actually reports, not from an estimate of ours. A small amount is held before the request goes out; once the response is complete, the final amount is charged and the rest is released. See Pricing.
6. If you hit an error
| Status | Meaning | What to do |
|---|---|---|
401 invalid_api_key | Key is wrong, revoked or expired | Create a new key and update .env |
402 insufficient_balance | Not enough balance to hold this request | Top up the wallet, or lower max_tokens |
404 model_not_found | The model id is wrong | Copy the id from /v1/models or the models page |
429 rate_limit_exceeded | Over the key's per-minute limit | Wait out Retry-After |
The full table lives in Errors and status codes.
Next steps
- Live responses with streaming and the optional
uttapen.metaevent that reports what each answer cost. - Coming from OpenAI or OpenRouter? The migration guide lists the handful of differences.
- Tool calling, images and files, JSON output and reasoning models.
- One page per language: Python, Node, PHP, Go, curl.
- Track usage and spend in Dashboard → Usage or through
GET /v1/uttapen/usage.