Response Headers
All AvalAI API responses include standard HTTP headers plus custom headers that provide important information about your requests, rate limits, and cost tracking.
Table of Contents
- Request Tracking Headers
- Migration Timeline
- API Metadata Headers
- Client-Supplied Request IDs
- Rate Limit Headers
- Project-Scoped Token Headers
- Header-Driven Retry Workflow
- Standard HTTP Headers
- Examples
- Best Practices
Request Tracking Headers
avalai-request-id
The most important header for cost tracking and debugging.
Every API response includes a unique avalai-request-id header that contains a UUID identifying that specific request. This ID is essential for:
- Precise Cost Tracking: Use it with
/user/v1/transactions/lookupto get exact cost details - Debugging: Reference this ID when reporting issues to support
- Request Correlation: Track requests across your systems
- Audit Trails: Maintain records of API calls
Format: UUID v7 (e.g., 01a009d5-ec91-74c2-8ffa-9eba731dfc9e)
Example:
avalai-request-id: 01a009d5-ec91-74c2-8ffa-9eba731dfc9eHeader change notice:
avalai-request-idreplaces the legacyx-request-idheader. During a 60-day transition window both headers are returned with the same value. See the Migration Timeline and the release announcement for details.
x-request-id (legacy, deprecated)
The x-request-id header is the legacy name for the AvalAI request ID. Because some CDNs also use x-request-id for their own tracing and can overwrite the header, it can no longer be treated as an unambiguous AvalAI identifier.
- Until 2026-10-15: AvalAI returns
x-request-idalongsideavalai-request-id, with the same UUID value. - After 2026-10-15: AvalAI stops returning
x-request-id. Anyx-request-idyou observe after that date was added by an intermediary such as a CDN and does not identify your AvalAI request.
Read avalai-request-id for cost lookup, support traces, and logging. If you need backward-compatible code during the transition window, fall back to x-request-id only while avalai-request-id is absent:
request_id = response.headers.get("avalai-request-id") or response.headers.get(
"x-request-id"
)Migration Timeline
Why the header changed
Some CDNs that sit in front of API origins set their own x-request-id header and can overwrite or reuse that header name for their internal tracing. When a request passes through such a CDN, the observed value may identify the CDN hop instead of the AvalAI request. To keep cost lookup and support traces unambiguous, AvalAI returns its request ID in the dedicated avalai-request-id header.
Transition window
| Phase | Dates | Response headers |
|---|---|---|
| Dual-header window opens | 2026-08-16 | Both avalai-request-id and x-request-id are returned with the same UUID value |
| Dual-header window closes | 2026-10-15 | Last day AvalAI returns x-request-id |
| Legacy header retired | After 2026-10-15 | Only avalai-request-id is returned by AvalAI |
Representative response headers during the transition window:
x-ratelimit-limit-requests: 1500
x-ratelimit-remaining-requests: 1499
x-ratelimit-limit-tokens: 30000000
x-ratelimit-remaining-tokens: 29999827
x-ratelimit-reset-requests: 50s
x-ratelimit-reset-tokens: 50s
x-request-id: 01a009d5-ec91-74c2-8ffa-9eba731dfc9e
avalai-request-id: 01a009d5-ec91-74c2-8ffa-9eba731dfc9eRequired action
- Update header readers to use
avalai-request-idbefore 2026-10-15. During the window, either header resolves to the same request. - Stop parsing, logging, or billing against
x-request-id; after the window it may belong to a CDN. - Keep the
request_idfields returned in response bodies (for example, the Videos API); they carry the same UUID value asavalai-request-idand are unaffected by this change. - No change is required for the
X-Client-Request-Idrequest header described below.
API Metadata Headers
Some OpenAI-compatible routes can return additional metadata headers. Treat them as useful diagnostics, not as required fields on every AvalAI provider route:
| Header | Meaning | How to use it |
|---|---|---|
openai-processing-ms | Upstream model-processing time in milliseconds. | Separate provider/model latency from your app, network, and queue time. |
openai-version | REST API version used by the upstream-compatible route. | Log during SDK or API migrations so behavior changes are easier to trace. |
openai-organization | Upstream organization associated with the request, when exposed. | Use only for debugging; do not depend on it for AvalAI account authorization. |
service_tier or tier metadata | Processing tier actually used for the request when returned by the route. | Compare requested vs. served processing mode in latency investigations. |
Do not build billing logic from these metadata headers. For AvalAI billing and reseller reconciliation, avalai-request-id plus the User API transaction lookup remains the authoritative path.
Client-Supplied Request IDs
OpenAI-compatible routes can also accept a request header named X-Client-Request-Id. Use it as your own trace ID: generate a unique value per API attempt, send it with the request, and log it beside the returned avalai-request-id.
This is useful when a timeout or network error prevents your application from receiving response headers. If the selected AvalAI route preserves upstream OpenAI-compatible metadata, support can use your client request ID as a second lookup key. Keep it ASCII-only, no longer than 512 characters, and unique per request. Do not replace avalai-request-id; the AvalAI response ID remains the authoritative ID for cost lookup and support.
curl https://api.avalai.ir/v1/responses \
-H "Authorization: Bearer $AVALAI_API_KEY" \
-H "Content-Type: application/json" \
-H "X-Client-Request-Id: 123e4567-e89b-12d3-a456-426614174000" \
-d '{
"model": "gpt-5.4-mini",
"input": "Return a one-line health check."
}'Accessing avalai-request-id via SDKs
When using official SDKs like the OpenAI Python SDK, capture the header through the raw-response API so your code reads avalai-request-id directly:
Python (OpenAI SDK)
import os
from openai import OpenAI
client = OpenAI(
api_key=os.environ["AVALAI_API_KEY"],
base_url="https://api.avalai.ir/v1",
)
raw_response = client.chat.completions.with_raw_response.create(
model="gpt-5.4-mini",
messages=[{"role": "user", "content": "Hello!"}],
)
completion = raw_response.parse()
# Access the request ID from the response headers
request_id = raw_response.headers.get("avalai-request-id")
print(f"Request ID: {request_id}")
# Output: Request ID: 01a009d5-ec91-74c2-8ffa-9eba731dfc9eNote
During the transition window you can also read the legacy value with raw_response.headers.get("x-request-id"); both resolve to the same UUID. After 2026-10-15 only avalai-request-id is returned, so migrate all readers before that date. SDK attributes derived from the legacy header name should not be relied upon after the window closes.
Responses API version
Use this version when the selected model supports /v1/responses. messages moves to input, and the final text is read from response.output_text.
import os
from openai import OpenAI
client = OpenAI(
api_key=os.environ["AVALAI_API_KEY"],
base_url="https://api.avalai.ir/v1",
)
response = client.responses.create(
model="gpt-5.4-mini",
instructions="You are a helpful assistant.",
input="Hello!",
)
print(response.output_text)messages→input- system message →
instructionsor adeveloperitem choices[0].message.content→response.output_text- for tools and multimodal output, inspect
response.outputby itemtype.
Accessing avalai-request-id with LangChain
LangChain does not directly expose raw HTTP response headers from the underlying API calls. However, you can capture the avalai-request-id header by using a custom HTTP client that intercepts the response headers.
Python LangChain v0.3 (Synchronous)
import httpx
from langchain_openai import ChatOpenAI
from langchain_core.callbacks import BaseCallbackHandler
from contextvars import ContextVar
import os
# Store headers in context var for thread safety
request_headers: ContextVar[dict] = ContextVar("request_headers", default={})
class HeaderCapturingClient(httpx.Client):
def send(self, request, **kwargs):
response = super().send(request, **kwargs)
request_headers.set(dict(response.headers))
return response
class HeaderAccessCallback(BaseCallbackHandler):
def __init__(self):
self.request_id = None
def on_llm_end(self, response, **kwargs):
headers = request_headers.get()
self.request_id = headers.get("avalai-request-id")
print(f"Captured avalai-request-id: {self.request_id}")
# Setup
http_client = HeaderCapturingClient()
chat_generator = ChatOpenAI(
base_url="https://api.avalai.ir/v1",
api_key=os.getenv("AVALAI_API_KEY"),
model="gpt-5.4-mini",
http_client=http_client,
)
callback = HeaderAccessCallback()
response = chat_generator.invoke("say hi", config={"callbacks": [callback]})
print(f"Request ID from callback: {callback.request_id}")Python LangChain v0.3 (Asynchronous)
import httpx
from contextvars import ContextVar
from langchain_openai import ChatOpenAI
from langchain_core.callbacks import AsyncCallbackHandler
import os
request_headers: ContextVar[dict] = ContextVar("request_headers", default={})
class AsyncHeaderCapturingClient(httpx.AsyncClient):
async def send(self, request, **kwargs):
response = await super().send(request, **kwargs)
request_headers.set(dict(response.headers))
return response
class AsyncHeaderAccessCallback(AsyncCallbackHandler):
def __init__(self):
self.request_id = None
async def on_llm_end(self, response, **kwargs):
headers = request_headers.get()
self.request_id = headers.get("avalai-request-id")
print(f"Captured avalai-request-id: {self.request_id}")
# Setup
http_client = AsyncHeaderCapturingClient()
chat_generator = ChatOpenAI(
base_url="https://api.avalai.ir/v1",
api_key=os.getenv("AVALAI_API_KEY"),
model="gpt-5.4-mini",
http_async_client=http_client,
)
# Usage (run in async context)
callback = AsyncHeaderAccessCallback()
response = await chat_generator.ainvoke("say hi", config={"callbacks": [callback]})How it works: This approach uses a custom
httpxclient that captures the response headers into a context variable. The LangChain callback then accesses these headers after the LLM call completes. TheContextVarensures thread safety when making concurrent requests. During the transition window you can fall back toheaders.get("x-request-id")if the new header is missing.
Accessing via HTTP Headers (Other SDKs)
For other SDKs or direct HTTP requests, access the header directly from the response:
| SDK/Method | How to Access |
|---|---|
| Python (OpenAI) | client.chat.completions.with_raw_response.create(...).headers.get("avalai-request-id") |
| Python (requests) | response.headers.get("avalai-request-id") |
| JavaScript (OpenAI) | (await ...create(...).withResponse()).response.headers.get("avalai-request-id") |
| JavaScript (fetch) | response.headers.get("avalai-request-id") |
| Go (net/http) | resp.Header.Get("avalai-request-id") |
| PHP (curl) | Extract with preg_match('/avalai-request-id:\s*([^\r\n]+)/i', ...) |
For Resellers: This header is crucial for your business. Always capture and store it to track the exact cost of each API call. The
/user/v1/transactions/lookupendpoint returns 100% accurate cost data within 30 seconds using this ID. See our Reseller Cost Tracking Guide for a complete workflow.
Rate Limit Headers
AvalAI implements rate limiting to ensure fair usage and system stability. Every API response includes headers that inform you about your current rate limit status.
Request-Based Rate Limits
| Header | Description | Example |
|---|---|---|
x-ratelimit-limit-requests | Maximum requests allowed in the time window | 30000 |
x-ratelimit-remaining-requests | Remaining requests in current window | 29999 |
x-ratelimit-reset-requests | Time until the request limit resets | 45s |
Token-Based Rate Limits
| Header | Description | Example |
|---|---|---|
x-ratelimit-limit-tokens | Maximum tokens allowed in the time window | 150000000 |
x-ratelimit-remaining-tokens | Remaining tokens in current window | 149999982 |
x-ratelimit-reset-tokens | Time until the token limit resets | 45s |
Project-Scoped Token Headers
Some upstream-compatible routes may include project-token headers when a project-level token bucket applies:
| Header | Description | Example |
|---|---|---|
x-ratelimit-limit-project-tokens | Maximum project-scoped tokens allowed in the current window | 60000 |
x-ratelimit-remaining-project-tokens | Remaining project-scoped tokens before throttling | 57000 |
x-ratelimit-reset-project-tokens | Time until the project-scoped token bucket resets | 3s |
If these headers are present, monitor them in addition to request and token headers. A request can be throttled because the project bucket is exhausted even when the route-level token bucket still has capacity.
Rate Limit Tiers
Your rate limits depend on your account tier (0-5). Higher tiers have higher limits. See Rate Limits Guide for detailed tier information.
429 Too Many Requests
If you exceed your rate limits, you'll receive a 429 status code with a Retry-After header indicating when you can retry:
HTTP/2 429
Retry-After: 45
x-ratelimit-limit-requests: 30000
x-ratelimit-remaining-requests: 0
x-ratelimit-reset-requests: 45sHeader-Driven Retry Workflow
Use response headers to make retries predictable instead of retrying blindly:
- Capture
avalai-request-idfirst. Log it before parsing the body so support, reseller cost lookup, and internal traces all point to the same request. - Respect
Retry-Afteron 429. Sleep for that value before retrying; if it is missing, use exponential backoff with jitter and a maximum retry count. - Check every bucket. A request can be blocked by request limits, token limits, or project-scoped token limits, so monitor
x-ratelimit-remaining-requests,x-ratelimit-remaining-tokens, and anyx-ratelimit-remaining-project-tokensheader. - Reduce token pressure. If token headers are the bottleneck, lower
max_tokens, shorten prompts, summarize prior turns, or move non-urgent bulk work to a batch-style workflow. - Fail gracefully. Return a clear "try again later" message to users when retries are exhausted and keep the captured
avalai-request-idin logs.
Standard HTTP Headers
Content-Type
Indicates the media type of the response body:
Content-Type: application/jsonContent-Length
Size of the response body in bytes:
Content-Length: 970Date
Server timestamp when the response was generated:
Date: Thu, 27 Nov 2025 09:24:15 GMTExamples
Complete Response Headers Example
Here's a complete example showing common headers from a typical API request. Some upstream-compatible metadata headers are route-dependent and may be absent. During the transition window both request ID headers are present:
# Use -i flag to show headers
curl -i "https://api.avalai.ir/v1/chat/completions" \
-H "Authorization: Bearer $AVALAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-5.4-mini",
"messages": [{"role": "user", "content": "hi"}]
}'
# Response Headers:
# HTTP/2 200
# date: Thu, 27 Nov 2025 09:24:15 GMT
# content-type: application/json
# content-length: 970
# openai-processing-ms: 842
# openai-version: 2020-10-01
# x-ratelimit-limit-requests: 30000
# x-ratelimit-remaining-requests: 29999
# x-ratelimit-limit-tokens: 150000000
# x-ratelimit-remaining-tokens: 149999982
# x-ratelimit-reset-requests: 45s
# x-ratelimit-reset-tokens: 45s
# x-ratelimit-remaining-project-tokens: 57000
# x-request-id: 01a009d5-ec91-74c2-8ffa-9eba731dfc9e
# avalai-request-id: 01a009d5-ec91-74c2-8ffa-9eba731dfc9e# Python example - accessing response headers
import requests
response = requests.post(
"https://api.avalai.ir/v1/chat/completions",
headers={"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"},
json={"model": "gpt-5.4-mini", "messages": [{"role": "user", "content": "hi"}]},
)
# Access headers
request_id = response.headers.get("avalai-request-id")
remaining_requests = response.headers.get("x-ratelimit-remaining-requests")
remaining_tokens = response.headers.get("x-ratelimit-remaining-tokens")
reset_time = response.headers.get("x-ratelimit-reset-requests")
print(f"Request ID: {request_id}")
print(f"Remaining Requests: {remaining_requests}")
print(f"Remaining Tokens: {remaining_tokens}")
print(f"Reset Time: {reset_time}")// JavaScript example - accessing response headers
const response = await fetch("https://api.avalai.ir/v1/chat/completions", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.AVALAI_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "gpt-5.4-mini",
messages: [{ role: "user", content: "hi" }],
}),
});
// Access headers
const requestId = response.headers.get("avalai-request-id");
const remainingRequests = response.headers.get("x-ratelimit-remaining-requests");
const remainingTokens = response.headers.get("x-ratelimit-remaining-tokens");
const resetTime = response.headers.get("x-ratelimit-reset-requests");
console.log(`Request ID: ${requestId}`);
console.log(`Remaining Requests: ${remainingRequests}`);
console.log(`Remaining Tokens: ${remainingTokens}`);
console.log(`Reset Time: ${resetTime}`);// Go example - accessing response headers
package main
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"os"
)
func main() {
body, _ := json.Marshal(map[string]interface{}{
"model": "gpt-5.4-mini",
"messages": []map[string]string{{"role": "user", "content": "hi"}},
})
req, _ := http.NewRequest("POST", "https://api.avalai.ir/v1/chat/completions", bytes.NewBuffer(body))
req.Header.Set("Authorization", "Bearer "+os.Getenv("AVALAI_API_KEY"))
req.Header.Set("Content-Type", "application/json")
resp, _ := (&http.Client{}).Do(req)
defer resp.Body.Close()
// Access headers
requestID := resp.Header.Get("avalai-request-id")
remainingRequests := resp.Header.Get("x-ratelimit-remaining-requests")
remainingTokens := resp.Header.Get("x-ratelimit-remaining-tokens")
resetTime := resp.Header.Get("x-ratelimit-reset-requests")
fmt.Printf("Request ID: %s\n", requestID)
fmt.Printf("Remaining Requests: %s\n", remainingRequests)
fmt.Printf("Remaining Tokens: %s\n", remainingTokens)
fmt.Printf("Reset Time: %s\n", resetTime)
}<?php
// PHP example - accessing response headers
$ch = curl_init('https://api.avalai.ir/v1/chat/completions');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Authorization: Bearer ' . getenv('AVALAI_API_KEY'),
'Content-Type: application/json'
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
'model' => 'gpt-5.4-mini',
'messages' => [['role' => 'user', 'content' => 'hi']]
]));
$response = curl_exec($ch);
$headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
$headers = substr($response, 0, $headerSize);
$body = substr($response, $headerSize);
curl_close($ch);
// Parse headers
preg_match('/avalai-request-id:\s*([^\r\n]+)/i', $headers, $requestId);
preg_match('/x-ratelimit-remaining-requests:\s*([^\r\n]+)/i', $headers, $remainingRequests);
preg_match('/x-ratelimit-remaining-tokens:\s*([^\r\n]+)/i', $headers, $remainingTokens);
preg_match('/x-ratelimit-reset-requests:\s*([^\r\n]+)/i', $headers, $resetTime);
echo "Request ID: " . trim($requestId[1] ?? '') . "\n";
echo "Remaining Requests: " . trim($remainingRequests[1] ?? '') . "\n";
echo "Remaining Tokens: " . trim($remainingTokens[1] ?? '') . "\n";
echo "Reset Time: " . trim($resetTime[1] ?? '') . "\n";
?>Responses API version
Use this version when the selected model supports /v1/responses. messages moves to input, and the final text is read from response.output_text.
import os
from openai import OpenAI
client = OpenAI(
api_key=os.environ["AVALAI_API_KEY"],
base_url="https://api.avalai.ir/v1",
)
response = client.responses.create(
model="gpt-5.4-mini",
instructions="You are a helpful assistant.",
input="Write a one-sentence summary of AvalAI.",
)
print(response.output_text)import OpenAI from "openai";
const client = new OpenAI({
apiKey: process.env.AVALAI_API_KEY,
baseURL: "https://api.avalai.ir/v1",
});
const response = await client.responses.create({
model: "gpt-5.4-mini",
instructions: "You are a helpful assistant.",
input: "Write a one-sentence summary of AvalAI.",
});
console.log(response.output_text);curl https://api.avalai.ir/v1/responses \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $AVALAI_API_KEY" \
-d '
{
"model": "gpt-5.4-mini",
"input": "Write a one-sentence summary of AvalAI.",
"instructions": "You are a helpful assistant."
}'messages→input- system message →
instructionsor adeveloperitem choices[0].message.content→response.output_text- for tools and multimodal output, inspect
response.outputby itemtype.
Monitoring Rate Limits
Example of monitoring your rate limits in real-time:
# Python - Rate limit monitoring
import requests
import time
def make_api_call():
response = requests.post(
"https://api.avalai.ir/v1/chat/completions",
headers={"Authorization": f"Bearer {api_key}"},
json={"model": "gpt-5.4-mini", "messages": [{"role": "user", "content": "hi"}]},
)
# Monitor rate limits
remaining = int(response.headers.get("x-ratelimit-remaining-requests", 0))
limit = int(response.headers.get("x-ratelimit-limit-requests", 0))
reset = response.headers.get("x-ratelimit-reset-requests", "")
usage_percent = ((limit - remaining) / limit * 100) if limit > 0 else 0
print(f"Rate Limit Usage: {usage_percent:.2f}%")
print(f"Remaining: {remaining}/{limit}")
print(f"Resets in: {reset}")
# Warn if approaching limit
if usage_percent > 90:
print("⚠️ WARNING: Approaching rate limit!")
time.sleep(5) # Back off
return response
# Use in your application
for i in range(100):
response = make_api_call()
# Process response...// JavaScript - Rate limit monitoring with backoff
async function makeApiCallWithRateLimit() {
const response = await fetch("https://api.avalai.ir/v1/chat/completions", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.AVALAI_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "gpt-5.4-mini",
messages: [{ role: "user", content: "hi" }],
}),
});
// Monitor rate limits
const remaining = parseInt(response.headers.get("x-ratelimit-remaining-requests") || "0");
const limit = parseInt(response.headers.get("x-ratelimit-limit-requests") || "0");
const reset = response.headers.get("x-ratelimit-reset-requests") || "";
const usagePercent = limit > 0 ? ((limit - remaining) / limit) * 100 : 0;
console.log(`Rate Limit Usage: ${usagePercent.toFixed(2)}%`);
console.log(`Remaining: ${remaining}/${limit}`);
console.log(`Resets in: ${reset}`);
// Handle 429 responses
if (response.status === 429) {
const retryAfter = parseInt(response.headers.get("retry-after") || "60");
console.log(`Rate limited. Retrying after ${retryAfter} seconds...`);
await new Promise((resolve) => setTimeout(resolve, retryAfter * 1000));
return makeApiCallWithRateLimit(); // Retry
}
// Warn if approaching limit
if (usagePercent > 90) {
console.log("⚠️ WARNING: Approaching rate limit!");
await new Promise((resolve) => setTimeout(resolve, 5000)); // Back off
}
return response;
}// Go - Rate limit monitoring
package main
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"os"
"strconv"
"time"
)
func makeAPICallWithRateLimit() (*http.Response, error) {
body, _ := json.Marshal(map[string]interface{}{
"model": "gpt-5.4-mini",
"messages": []map[string]string{{"role": "user", "content": "hi"}},
})
req, _ := http.NewRequest("POST", "https://api.avalai.ir/v1/chat/completions", bytes.NewBuffer(body))
req.Header.Set("Authorization", "Bearer "+os.Getenv("AVALAI_API_KEY"))
req.Header.Set("Content-Type", "application/json")
resp, err := (&http.Client{}).Do(req)
if err != nil {
return nil, err
}
// Monitor rate limits
remaining, _ := strconv.Atoi(resp.Header.Get("x-ratelimit-remaining-requests"))
limit, _ := strconv.Atoi(resp.Header.Get("x-ratelimit-limit-requests"))
reset := resp.Header.Get("x-ratelimit-reset-requests")
usagePercent := float64(0)
if limit > 0 {
usagePercent = float64(limit-remaining) / float64(limit) * 100
}
fmt.Printf("Rate Limit Usage: %.2f%%\n", usagePercent)
fmt.Printf("Remaining: %d/%d\n", remaining, limit)
fmt.Printf("Resets in: %s\n", reset)
// Handle 429 responses
if resp.StatusCode == 429 {
retryAfter, _ := strconv.Atoi(resp.Header.Get("retry-after"))
if retryAfter == 0 {
retryAfter = 60
}
fmt.Printf("Rate limited. Retrying after %d seconds...\n", retryAfter)
time.Sleep(time.Duration(retryAfter) * time.Second)
resp.Body.Close()
return makeAPICallWithRateLimit() // Retry
}
// Warn if approaching limit
if usagePercent > 90 {
fmt.Println("⚠️ WARNING: Approaching rate limit!")
time.Sleep(5 * time.Second) // Back off
}
return resp, nil
}<?php
// PHP - Rate limit monitoring
function makeApiCallWithRateLimit() {
$ch = curl_init('https://api.avalai.ir/v1/chat/completions');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Authorization: Bearer ' . getenv('AVALAI_API_KEY'),
'Content-Type: application/json'
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
'model' => 'gpt-5.4-mini',
'messages' => [['role' => 'user', 'content' => 'hi']]
]));
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
$headers = substr($response, 0, $headerSize);
$body = substr($response, $headerSize);
curl_close($ch);
// Parse headers
preg_match('/x-ratelimit-remaining-requests:\s*(\d+)/i', $headers, $remaining);
preg_match('/x-ratelimit-limit-requests:\s*(\d+)/i', $headers, $limit);
preg_match('/x-ratelimit-reset-requests:\s*([^\r\n]+)/i', $headers, $reset);
$remaining = isset($remaining[1]) ? (int)$remaining[1] : 0;
$limit = isset($limit[1]) ? (int)$limit[1] : 0;
$reset = isset($reset[1]) ? trim($reset[1]) : '';
$usagePercent = $limit > 0 ? (($limit - $remaining) / $limit * 100) : 0;
echo "Rate Limit Usage: " . number_format($usagePercent, 2) . "%\n";
echo "Remaining: $remaining/$limit\n";
echo "Resets in: $reset\n";
// Handle 429 responses
if ($httpCode == 429) {
preg_match('/retry-after:\s*(\d+)/i', $headers, $retryAfter);
$retryAfter = isset($retryAfter[1]) ? (int)$retryAfter[1] : 60;
echo "Rate limited. Retrying after $retryAfter seconds...\n";
sleep($retryAfter);
return makeApiCallWithRateLimit(); // Retry
}
// Warn if approaching limit
if ($usagePercent > 90) {
echo "⚠️ WARNING: Approaching rate limit!\n";
sleep(5); // Back off
}
return json_decode($body, true);
}
?>Responses API version
Use this version when the selected model supports /v1/responses. messages moves to input, and the final text is read from response.output_text.
import os
from openai import OpenAI
client = OpenAI(
api_key=os.environ["AVALAI_API_KEY"],
base_url="https://api.avalai.ir/v1",
)
response = client.responses.create(
model="gpt-5.4-mini",
instructions="You are a helpful assistant.",
input="Write a one-sentence summary of AvalAI.",
)
print(response.output_text)import OpenAI from "openai";
const client = new OpenAI({
apiKey: process.env.AVALAI_API_KEY,
baseURL: "https://api.avalai.ir/v1",
});
const response = await client.responses.create({
model: "gpt-5.4-mini",
instructions: "You are a helpful assistant.",
input: "Write a one-sentence summary of AvalAI.",
});
console.log(response.output_text);curl https://api.avalai.ir/v1/responses \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $AVALAI_API_KEY" \
-d '
{
"model": "gpt-5.4-mini",
"input": "Write a one-sentence summary of AvalAI.",
"instructions": "You are a helpful assistant."
}'messages→input- system message →
instructionsor adeveloperitem choices[0].message.content→response.output_text- for tools and multimodal output, inspect
response.outputby itemtype.
Best Practices
1. Always Capture avalai-request-id
Store the avalai-request-id from every API call for:
- Cost tracking and billing (especially for resellers)
- Debugging and support requests
- Audit trails and compliance
During the transition window (until 2026-10-15) you may fall back to x-request-id when the new header is missing, but remove that fallback before the window closes. If you also send X-Client-Request-Id, log both IDs together so timeout reports and successful responses can be correlated.
# Good practice
request_id = response.headers.get("avalai-request-id")
db.store_request_log(user_id=user.id, request_id=request_id, timestamp=now())2. Monitor Rate Limits Proactively
Don't wait for 429 errors. Monitor your rate limit headers and implement backoff strategies:
remaining = int(response.headers.get("x-ratelimit-remaining-requests", 0))
if remaining < 100: # Less than 100 requests remaining
time.sleep(1) # Back off3. Handle 429 Responses Gracefully
Implement exponential backoff when you receive rate limit errors:
import time
def make_request_with_retry(max_retries=3):
for attempt in range(max_retries):
response = requests.post(...)
if response.status_code == 429:
retry_after = int(response.headers.get("retry-after", 60))
time.sleep(retry_after)
continue
return response
raise Exception("Max retries exceeded")4. Use Request IDs for Cost Lookups
For precise cost tracking, wait up to 30 seconds after the request, then query the User API:
# Step 1: Make API call and capture avalai-request-id
response = requests.post(...)
request_id = response.headers.get("avalai-request-id")
# Step 2: Wait for processing
time.sleep(5) # Usually available much sooner
# Step 3: Get exact cost
cost_data = requests.post(
"https://api.avalai.ir/user/v1/transactions/lookup",
json={"transaction_ids": [request_id]},
)See Reseller Cost Tracking Guide for complete workflow.
5. Log Headers for Debugging
When reporting issues to support, include relevant headers:
import logging
logging.info(f"Request ID: {response.headers.get('avalai-request-id')}")
logging.info(f"Status Code: {response.status_code}")
logging.info(f"Rate Limit: {response.headers.get('x-ratelimit-remaining-requests')}")Related Resources
- Request ID Header Migration Announcement -
avalai-request-idreplacesx-request-id - User API Reference - Track usage and costs using request IDs
- Rate Limits Guide - Understanding rate limit tiers and best practices
- Reseller Cost Tracking Guide - Step-by-step guide for precise cost tracking
- Error Handling - Best practices for handling API errors
- Chat Completions API - Main API endpoint documentation