Source: https://docs.apertis.ai/intro # Quick Overview ### 1. Homepage - **[Apertis](https://apertis.ai/)** Click **[Apertis](https://apertis.ai/)** to visit the official website, where you can find the currently supported **applications**, the **[model support list](https://apertis.ai/models)**, and **more information**. ### 2. Login Click on the "Login" button in the top-right corner to register and log in. Currently, **email** and **username** login methods are supported. ### 3. Keys After completing registration, your API Key information will be displayed here. **A default API Key** is provided by default. Click `Copy` to obtain your default API Key, which starts with `sk-*`. **Please keep it secure.** If you want to create additional API Keys, you can click `Add API Key`. You can customize your API Key settings, including **quota allocation**, **model usage restrictions**, **IP access restrictions**, and **expiration date**. This allows you to allocate API Keys to different users with specific quotas and usage permissions. ### 4. Credits You can add credits using credit cards or local payment methods (Visa, MasterCard, JCB, American Express, etc.), with all transactions securely processed through **Stripe**. You can revisit this section at any time to top up or **check your remaining credits**. ### 5. Logs Through the **Logs** section, you can monitor the **usage history**, **token consumption**, and **credit usage** for each API Key. --- Source: https://docs.apertis.ai/getting-started/quick-start # Quick Start Get up and running with the Apertis API in under 5 minutes. This guide walks you through making your first API call. ## Prerequisites - An Apertis account ([Create one](https://apertis.ai/register); no card is required to register) - Model access through a [Coding Plan](https://apertis.ai/subscribe) or [PAYG balance](https://apertis.ai/setting?tab=credits) - An API key from [Settings → API Keys](https://apertis.ai/setting?tab=keys) - Basic knowledge of HTTP requests or a programming language ## Step 1: Create Your Account and Choose Access 1. [Create an Apertis account](https://apertis.ai/register) or [log in](https://apertis.ai/login). 2. Choose a [Coding Plan](https://apertis.ai/subscribe) or add [PAYG balance](https://apertis.ai/setting?tab=credits) before your first API request. 3. Use the [Models page](https://apertis.ai/models) to confirm which model IDs are available for your key type. Creating an account does not require a card. A plan or PAYG balance is still required before a paid model request can succeed. ## Step 2: Create Your API Key 1. Open [**Settings → API Keys**](https://apertis.ai/setting?tab=keys). 2. Click **Create New Key**. 3. Copy your key (format: `sk-xxxxxxxx`). > **Tip: Subscription users** > > If you have an active subscription, a dedicated key (`sk-sub-...`) is already created for you. Find it in **Settings** → **API Keys** tab (click **My Plan** in the navbar). > **Warning** > > Save your API key securely. It's only shown once! ## Step 3: Make Your First Request Choose your preferred method: ### Using cURL ```bash curl https://api.apertis.ai/v1/chat/completions \ -H "Content-Type: application/json" \ -H "Authorization: Bearer sk-your-api-key" \ -d '{ "model": "gpt-5.5", "messages": [ {"role": "user", "content": "Hello! What can you do?"} ] }' ``` ### Using Python First, install the OpenAI SDK: ```bash pip install openai ``` Then make a request: ```python from openai import OpenAI client = OpenAI( api_key="sk-your-api-key", base_url="https://api.apertis.ai/v1" ) response = client.chat.completions.create( model="gpt-5.5", messages=[ {"role": "user", "content": "Hello! What can you do?"} ] ) print(response.choices[0].message.content) ``` ### Using Node.js First, install the OpenAI SDK: ```bash npm install openai ``` Then make a request: ```javascript import OpenAI from 'openai'; const client = new OpenAI({ apiKey: 'sk-your-api-key', baseURL: 'https://api.apertis.ai/v1' }); async function main() { const response = await client.chat.completions.create({ model: 'gpt-5.5', messages: [ { role: 'user', content: 'Hello! What can you do?' } ] }); console.log(response.choices[0].message.content); } main(); ``` ## Step 4: Verify the Response and Activity Record A successful response looks like this: ```json { "id": "chatcmpl-abc123", "object": "chat.completion", "created": 1703894400, "model": "gpt-5.5", "choices": [ { "index": 0, "message": { "role": "assistant", "content": "Hello! I'm an AI assistant. I can help you with..." }, "finish_reason": "stop" } ], "usage": { "prompt_tokens": 12, "completion_tokens": 45, "total_tokens": 57 } } ``` ### Key Fields | Field | Description | |-------|-------------| | `id` | Unique identifier for this completion | | `model` | The model used for generation | | `choices[0].message.content` | The AI's response | | `usage` | Token usage for billing | After the request succeeds, open [**Settings → Activity**](https://apertis.ai/setting?tab=activity) and confirm the matching Activity record. The integration is complete only when you have both a successful response and the matching Activity record; account creation by itself is not activation. ## Step 5: Try Different Models Apertis provides a live model catalog across multiple providers. Availability changes over time and can vary by key type, so check the [Models page](https://apertis.ai/models) or query `GET /v1/models` before pinning an ID in production. ```python # OpenAI GPT-5.5 response = client.chat.completions.create( model="gpt-5.5", messages=[{"role": "user", "content": "Explain quantum computing"}] ) # Anthropic Claude Sonnet 4.6 response = client.chat.completions.create( model="claude-sonnet-4-6", messages=[{"role": "user", "content": "Explain quantum computing"}] ) # Google Gemini Pro response = client.chat.completions.create( model="gemini-3.1-pro-preview", messages=[{"role": "user", "content": "Explain quantum computing"}] ) ``` ### Popular Models | Model | Best For | |-------|----------| | `gpt-5.5` | General purpose, balanced | | `gpt-5.4-mini` | Fast, cost-effective | | `claude-sonnet-4-6` | Long context, analysis | | `claude-opus-4-8` | Complex reasoning | | `gemini-3.1-pro-preview` | Multimodal, long context | [View all models →](/installation/models) ## Step 6: Enable Streaming For real-time responses, enable streaming: ```python response = client.chat.completions.create( model="gpt-5.5", messages=[{"role": "user", "content": "Write a short poem"}], stream=True # Enable streaming ) for chunk in response: if chunk.choices[0].delta.content: print(chunk.choices[0].delta.content, end="") ``` ## Common Use Cases ### Multi-turn Conversations ```python messages = [ {"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": "What's the capital of France?"}, {"role": "assistant", "content": "The capital of France is Paris."}, {"role": "user", "content": "What's the population?"} ] response = client.chat.completions.create( model="gpt-5.5", messages=messages ) ``` ### Code Generation ```python response = client.chat.completions.create( model="gpt-5.5", messages=[{ "role": "user", "content": "Write a Python function to calculate fibonacci numbers" }] ) ``` ### Image Analysis ```python response = client.chat.completions.create( model="gpt-5.5", messages=[{ "role": "user", "content": [ {"type": "text", "text": "What's in this image?"}, {"type": "image_url", "image_url": {"url": "https://example.com/image.jpg"}} ] }] ) ``` ### Text Embeddings ```python response = client.embeddings.create( model="jina-embeddings-v3", input="Hello, world!" ) embedding = response.data[0].embedding print(f"Embedding dimension: {len(embedding)}") ``` ## Environment Variables For production, use environment variables instead of hardcoding: ```bash # Set environment variable export APERTIS_API_KEY="sk-your-api-key" ``` ```python import os from openai import OpenAI client = OpenAI( api_key=os.environ.get("APERTIS_API_KEY"), base_url="https://api.apertis.ai/v1" ) ``` ## Error Handling Always handle potential errors: ```python from openai import OpenAI, APIError, RateLimitError client = OpenAI( api_key="sk-your-api-key", base_url="https://api.apertis.ai/v1" ) try: response = client.chat.completions.create( model="gpt-5.5", messages=[{"role": "user", "content": "Hello!"}] ) print(response.choices[0].message.content) except RateLimitError: print("Rate limited! Please wait and retry.") except APIError as e: print(f"API error: {e}") ``` ## Next Steps Now that you've made your first API call, explore more: - [API Keys](/authentication/api-keys) - Manage and secure your keys - [Models](/installation/models) - Browse all available models - [Subscription Plans](/billing/subscription-plans) - Choose the right plan - [Streaming](/api/text-generation/streaming) - Real-time response streaming - [Integrations](/installation/cline) - Use with Cursor, Cline, and more ## Quick Reference ### Base URL ``` https://api.apertis.ai/v1 ``` ### Authentication ``` Authorization: Bearer sk-your-api-key ``` ### Key Endpoints | Endpoint | Description | |----------|-------------| | `/v1/chat/completions` | Chat completions | | `/v1/embeddings` | Text embeddings | | `/v1/images/generations` | Image generation | | `/v1/audio/speech` | Text to speech | | `/v1/audio/transcriptions` | Speech to text | | `/v1/models` | List available models | ## Getting Help - [Troubleshooting Guide](/help/troubleshooting) - [Error Codes Reference](/help/error-codes) - [Contact Support](mailto:hi@apertis.ai) --- Source: https://docs.apertis.ai/installation/models # Models Access models from OpenAI, Google, Anthropic, Meta, Microsoft, Mistral, Alibaba, NVIDIA, xAI, Amazon, DeepSeek, Perplexity, Allen AI, and other providers through [**Apertis**](https://apertis.ai). We aim to provide users with a unified API gateway to access all mainstream models while offering a more affordable, faster, and more stable service. Explore the current catalog, with prices and context windows, on the [**Models page**](/models), or browse it on [apertis.ai](https://apertis.ai/models). Your API key type determines which models are available. --- Source: https://docs.apertis.ai/principles # Why Choose Apertis? ## 🚀 Perfect Balance of Performance and Cost - **Smart Price Optimization**: Find the most competitive prices across multiple providers - **Ultimate Performance**: Ensure minimal latency and maximum throughput - **Flexible Priority Settings**: Freedom to balance these factors according to your needs ## 💡 Seamless Integration Experience - **OpenAI Compatible API**: Switch between models or providers without code modifications - **User Autonomy**: Enable end-users to choose and pay for their preferred models ## 🔍 Leading Market Insights - **Early Model Access**: Be the first to experience the latest AI models - **Real-time Usage Analytics**: Track actual application data across different models and scenarios ## 💳 Simplified Billing System - **Unified Billing Management**: Simple and transparent billing regardless of provider count - **Clear Cost Control**: Easily track and manage your AI expenditure ## 🛡️ Enterprise-Grade Reliability - **Smart Failover**: Automatic switching ensures service continuity during provider outages - **Advanced Routing**: Intelligent request distribution for consistent processing ## ⚡ Enhanced Usage Limits - **Optimized Processing Capacity**: Direct provider partnerships for higher request limits - **Increased Throughput**: Ensure your applications can handle larger workloads --- Source: https://docs.apertis.ai/usage # Models Intro Leading large language model providers on the market include **[OpenAI](https://openai.com/)**, **[Anthropic](https://www.anthropic.com/)**, **[Google](https://gemini.google.com/)**, **[Meta](https://www.llama.com/)**, **[Microsoft](https://www.microsoft.com/en-us/ai/microsoft-365-copilot)**, **[Mistral](https://mistral.ai/)**, and **[Alibaba](https://www.alibabacloud.com/)**. Each offers AI solutions with unique strengths tailored to various application needs. ### Recommended Models - **Knowledge Queries, Content Generation, Daily Assistance**: **OpenAI GPT-5.5**, Model ID: `gpt-5.5`. - **Programming and Development**: **Anthropic Claude Sonnet 4.6**, Model IDs: `claude-sonnet-4-6`, `claude-haiku-4-5`. - **Translation (Web Content, Subtitles, Research Papers)**: **Google Gemini 3.5 Flash**, Model IDs: `gemini-3.5-flash`, `gpt-5.4-mini`. ### Model Naming Rules Models are typically named with the following structure: - **Model Name**: Corresponds to the developer or provider, such as `GPT*` (**OpenAI**), `Claude` (**Anthropic**), `Gemini` (**Google**), `Llama*` (**Meta**), etc. - **Version Number**: Larger version numbers indicate significant improvements in capabilities, such as enhanced reasoning abilities. For instance, a higher version like `gpt-5.5` is more capable than an earlier release such as `gpt-5.4`. - **Sub-Version**: Sub-versions exhibit minor performance differences compared to main versions. Naming conventions vary, e.g., `gpt-5.4-mini`, `gpt-5.4-nano`. For **Gemini**, terms like **mini, flash** denote fewer parameters and lower costs. Context lengths like **32k, 128k, 4096** indicate the maximum token length the model supports. ### **OpenAI** - **Use Cases**: OpenAI models are widely used in **chatbots, content generation, and personal assistants**. - **Recommended Models**: `gpt-5.5`, `gpt-5.4-mini`, `gpt-5.4-nano`. ### **Anthropic** - **Use Cases**: **Claude** excels as a **programming assistant**, leveraging Retrieval Augmented Generation (RAG) to understand codebases and provide solutions. Paired with tools like Cursor for RAG analysis, it can efficiently analyze entire codebases, solve issues, and improve development workflows. - **Recommended Models**: `claude-opus-4-8`, `claude-sonnet-4-6`, `claude-fable-5`. ### **Google** - **Use Cases**: Gemini models offer general-purpose capabilities with competitive pricing. They are ideal for **translating web content and processing large PDFs** (e.g., research papers, patents). - **Recommended Models**: `gemini-3.5-flash`, `gemini-3.1-pro-preview`. --- Source: https://docs.apertis.ai/authentication/organizations # Organizations, Members, Projects, Groups, and Roles Organizations let you manage shared API usage, billing, members, projects, API keys, and access control from one dashboard. ## Personal and Team Organizations Every account has a default **Personal organization**. You can use it as your default workspace for personal API usage and access management. Personal organizations have a permanent owner: - The owner cannot leave or remove themselves from the Personal organization. - The owner can invite and remove other members. - Removing a member disables the member's organization-scoped API keys and admin keys for that organization. Team organizations are separate shared workspaces. By default, each user can create up to **5 team organizations**. ## Members Members are users who have joined an organization. Owners can: - Invite members by email. - Edit a member's organization role. - Remove members other than themselves from the organization. - Assign additional access through custom roles and groups. Readers can view organization resources but cannot manage billing, members, groups, roles, projects, or keys unless an owner assigns additional custom permissions. ## Invitations Invitations are pending membership requests. An invitation becomes a member only after the recipient accepts it. Invitation flow: 1. An owner sends one or more invitations by email. 2. If the recipient already has an Apertis account, they sign in and accept the invitation. 3. If the recipient does not have an account, they create one from the invite flow, then join the organization. 4. The new member appears in the Members tab after accepting. Pending invitations can be revoked individually or in bulk. Revoking a pending invitation does not affect existing members. Default limits: | Limit | Default | | ----------------------------------------- | ------- | | Personal organization members | 50 | | Personal organization pending invitations | 50 | | Team organization pending invitations | 50 | | Team organizations per user | 5 | Limits may be adjusted for enterprise customers. ## Projects Projects are usage and resource scopes inside an organization. Use them to separate environments, products, customers, or internal teams while keeping billing under the same organization. Every organization has a default project. The default project cannot be archived or removed. Owners and members with project management permission can create additional projects. Owners can: - Create projects for shared workloads. - Set an optional monthly project cap. - Open **Manage project** to manage project API keys, project people, and service accounts. - Archive non-default projects. Project rules: - Project members must already be organization members. - Adding someone to a project does not invite them to the organization. - Project-scoped API keys bill to the organization and can be limited to that project. - Service accounts belong to a project and are intended for server-side automation. - Archiving a project disables API keys scoped to that project. - Monthly project caps are an extra spending gate. They do not change which organization balance or payment method is billed. Large organization project lists are loaded with server-side pagination and search. Search by project name, slug, public project ID, or region to avoid loading every project into the browser. ## Groups Groups collect organization members so you can assign access to multiple people at once. Groups only contain users who are already organization members. Adding someone to a group does not invite them to the organization; invite and accept the member first, then add them to groups. Owners can: - Create a group with a name and description. - Add or remove joined organization members through multi-select. - Enable or disable a group. - Delete a group. Disabling a group pauses that group's role assignments. Members remain in the organization, and group membership is preserved. Deleting a group removes the group and its group role assignments; it does not remove members from the organization. ## Roles Roles define reusable permission bundles. They are assigned to members or groups. System roles provide baseline access: | Role | Purpose | | ------ | --------------------------------------------------------------------------------------- | | Owner | Full organization administration, including members, billing, projects, roles, and keys | | Reader | Read-only organization access | Custom roles let owners grant more specific capabilities, such as billing management, project management, member management, API key management, or admin key management. Role behavior: - Custom roles can be assigned directly to one member. - Custom roles can be assigned to a group, and every enabled group member inherits the role. - Effective access is additive: direct member roles plus enabled group roles. - Editing a custom role changes access for all assigned members and groups immediately. - Deleting a custom role removes that role from assigned targets immediately. ## Projects vs. Groups vs. Roles These controls solve different problems and are designed to be used together. | Control | What it is | Use it for | | ------------ | ----------------------------------------------- | ------------------------------------------------------------------- | | **Projects** | API usage, key, people, and service scopes | Separating production, staging, customers, products, or workloads | | **Groups** | Collections of organization members | Assigning the same access to multiple people | | **Roles** | Reusable permission bundles | Defining what a member or group can do | | **Keys** | Credentials used by apps, services, and people | Authenticating API requests or organization administration requests | Effective access is additive: - Organization role gives baseline access. - Direct custom roles add capabilities to one member. - Enabled group role assignments add capabilities to every member in that group. - Project membership limits access to project-scoped resources. - API key permission mode limits what that key can do, even if the creator has broader dashboard access. ## Recommended Access Flow Use this sequence for predictable organization access: 1. Invite users to the organization. 2. Wait for users to accept and appear as members. 3. Create projects for workloads or environments. 4. Add joined members to the projects they need. 5. Create groups for teams, functions, or environments. 6. Add members to groups. 7. Create custom roles when the default Owner and Reader roles are too broad. 8. Assign roles to members or groups. 9. Create project keys or service accounts for shared services. 10. Review API keys, admin keys, usage, and audit logs after access changes. ## API Keys and Member Removal Organization API keys are tied to an organization and, when applicable, to a project and owner. When a member is removed from an organization: - Their organization-scoped API keys are disabled. - Their organization admin keys are revoked. - Requests using those disabled keys are rejected. - The organization audit log records the access change. This keeps billing and access aligned with current organization membership. --- Source: https://docs.apertis.ai/authentication/api-keys # API Keys API keys are used to authenticate requests to the Apertis API. This guide explains how to obtain, manage, and secure your API keys. ## Obtaining Your API Key ### Standard API Key (PAYG) 1. **Sign up or log in** to your account at [Apertis](https://apertis.ai/login) 2. Navigate to [**API Keys**](https://apertis.ai/setting?tab=keys) 3. Click **Create New Key** to generate a new API key 4. Copy and securely store your API key - it will only be shown once ### Subscription API Key If you have an active subscription, a dedicated API key is automatically created for you: 1. Click **My Plan** in the navbar (or go to [**Settings**](https://apertis.ai/setting?tab=keys)) 2. Switch to the **API Keys** tab 3. Your subscription key (`sk-sub-...`) is listed with integration guides and ready to use > **Warning: Important** > > Your API key is displayed only once upon creation. Store it securely immediately. If you lose your key, you'll need to create a new one. ## API Key Format Apertis API keys follow this format: ``` sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ``` - **Prefix**: `sk-` (standard key) or `sk-sub-` (subscription key) - **Length**: 32+ characters after the prefix - **Characters**: Alphanumeric (a-z, A-Z, 0-9) ## Using Your API Key ### Authentication Methods Apertis supports two authentication methods: | Method | Header | Example | | ------------ | --------------- | --------------------------------------- | | Bearer Token | `Authorization` | `Authorization: Bearer sk-your-api-key` | | API Key | `x-api-key` | `x-api-key: sk-your-api-key` | Both methods work across all endpoints. The `x-api-key` header provides compatibility with Anthropic SDKs. ### Example: Bearer Token (OpenAI SDKs) ```bash curl https://api.apertis.ai/v1/chat/completions \ -H "Authorization: Bearer sk-your-api-key" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-5.5", "messages": [{"role": "user", "content": "Hello!"}] }' ``` ### Example: x-api-key (Anthropic SDKs) ```bash curl https://api.apertis.ai/v1/messages \ -H "x-api-key: sk-your-api-key" \ -H "Content-Type: application/json" \ -d '{ "model": "claude-sonnet-4-6", "max_tokens": 1024, "messages": [{"role": "user", "content": "Hello!"}] }' ``` > **Tip** > > When both headers are provided, `Authorization` takes precedence over `x-api-key`. ### Python Example ```python from openai import OpenAI client = OpenAI( api_key="sk-your-api-key", base_url="https://api.apertis.ai/v1" ) response = client.chat.completions.create( model="gpt-5.5", messages=[{"role": "user", "content": "Hello!"}] ) print(response.choices[0].message.content) ``` ### Node.js Example ```javascript import OpenAI from "openai"; const client = new OpenAI({ apiKey: "sk-your-api-key", baseURL: "https://api.apertis.ai/v1", }); const response = await client.chat.completions.create({ model: "gpt-5.5", messages: [{ role: "user", content: "Hello!" }], }); console.log(response.choices[0].message.content); ``` ## API Key Settings ### Quota Management Each API key has its own quota allocation: | Setting | Description | | ------------------- | ------------------------------- | | **Remaining Quota** | Available balance for API calls | | **Used Quota** | Total amount consumed | | **Unlimited** | Option to set unlimited quota | ### Model Restrictions You can restrict which models an API key can access: - **All Models**: Access to all available models (default) - **Specific Models**: Whitelist only certain models Example: Restrict a key to only use `gpt-5.5` and `claude-sonnet-4-6`: ``` Model Whitelist: gpt-5.5, claude-sonnet-4-6 ``` ### IP Whitelisting (Subnet Restrictions) For enhanced security, you can restrict API key usage to specific IP addresses or subnets: ``` Allowed IPs: 192.168.1.0/24, 10.0.0.1 ``` This ensures your API key can only be used from authorized networks. ### Expiration Date Set an expiration date for temporary or project-specific keys: - **Never Expire**: Key remains valid indefinitely - **Custom Date**: Key expires on a specific date ## Types of API Keys ### Standard API Keys (`sk-`) - Created manually by users - Configurable quota and restrictions - Suitable for development and production use ### Subscription API Keys (`sk-sub-`) - Automatically generated for subscription plans - Quota synced with subscription cycle - Automatically reset when billing cycle renews ### Organization API Keys (`sk-org_`) - Created from an organization, usually scoped to a project - Bill to the organization instead of only the individual user - Can use permission modes such as **All**, **Restricted**, or **Read only** - Are disabled when their owning member is removed from the organization ### Organization Admin Keys (`sk-admin_`) - Created from an organization by an owner or member with admin-key permission - Used for organization administration automation, not normal model inference traffic - Can manage supported organization resources through Admin API workflows - Can be limited with **All**, **Restricted**, or **Read only** permission modes - Should be stored only in trusted server-side infrastructure Organization keys have two views: | View | Use | | -------------------- | -------------------------------------------------------------- | | **Project API Keys** | Keys scoped to organization projects and shared team workloads | | **User API Keys** | Keys tied to individual members for organization usage | ### Organization API Keys Tab Rules The organization **API Keys** tab separates shared project keys from member-owned user keys so billing, audit logs, and access control stay clear. #### Project keys Project keys are owned by the organization project, not by an individual member. Use project keys for shared workloads such as production backends, staging services, CI jobs, server-side integrations, or applications maintained by a team. Project key behavior: - Owners and members with API key management permission can create project keys. - Project keys bill to the organization. - Project keys can be restricted to a project and permission mode, such as **All**, **Restricted**, or **Read only**. - The **Created by** column records who created the key, but the key remains project-owned. - Disabling or deleting a project key immediately stops requests that use it. - Project keys are the preferred choice for long-running services because they do not depend on one member's personal key lifecycle. - Project keys can be reviewed from the organization API Keys tab or from **Manage project** on the project row. Use a project key when: - A service is shared by multiple people. - The key is deployed to backend infrastructure. - Usage should continue even if the original creator changes teams. - You need organization-level billing and audit visibility. #### Project service accounts Service accounts are project-owned identities for automation. Use a service account when a backend service, worker, CI job, or integration should own the credential instead of a human member. Service account behavior: - A service account belongs to one project. - Its keys bill to the organization and are scoped to that project. - Deleting the service account disables keys owned by it. - The audit trail identifies the service account instead of a personal user key. #### User keys User keys are owned by individual organization members. Use user keys for personal development, CLI usage, local testing, notebooks, or workflows where the audit trail should point directly to one member. User key behavior: - A user key must be created by the member who owns it. - Organization owners cannot create user-owned secrets on behalf of another member. - The key's effective access follows the member's organization, project, group, and role permissions. - If the member is removed from the organization, the member's organization-scoped user keys are disabled for that organization. - Requests made with a user key are attributed to that user in audit and usage review. Use a user key when: - One person is experimenting or developing locally. - The key should stop working when that person's access is removed. - You need user-level attribution instead of a shared service identity. > **Tip** > > If an owner needs a key for a shared service, create a **Project API Key**. If a member needs a personal development key, that member should create a **User API Key** from their own account context. ### Organization Admin Keys Tab Rules Admin keys are for organization administration automation, such as managing projects, project members, service accounts, project API keys, audit-log access, or other supported Admin API operations. Admin key behavior: - Admin keys use the `sk-admin_` prefix. - The plaintext secret is shown only once. - Read-only admin keys can inspect supported admin resources but cannot write changes. - Restricted admin keys should grant only the scopes the automation needs. - Revoking an admin key immediately rejects future requests using it. - If the creating member is removed from the organization, admin access tied to that membership is revoked. Use an admin key when: - A trusted backend needs to automate organization administration. - You need CI or internal tooling to manage projects, service accounts, or project API keys. - A human owner should not paste their personal session or user key into automation. Do not use admin keys in browsers, mobile apps, customer devices, or model inference clients. For organization access rules, see [Organizations, Members, Projects, Groups, and Roles](/authentication/organizations). ## Best Practices ### Security Guidelines 1. **Never expose your API key in client-side code** ```javascript // DON'T do this in frontend code const apiKey = "sk-your-api-key"; // Exposed to users! ``` 2. **Use environment variables** ```bash export APERTIS_API_KEY="sk-your-api-key" ``` ```python import os api_key = os.environ.get("APERTIS_API_KEY") ``` 3. **Rotate keys regularly** - Create new keys periodically and deprecate old ones 4. **Use separate keys for different environments** - Development key with limited quota - Production key with appropriate restrictions 5. **Enable IP whitelisting** for production keys when possible ### Quota Management Tips - Monitor your usage regularly in the dashboard - Set up alerts for low quota warnings - Use model restrictions to prevent unexpected costs - Consider using subscription plans for predictable budgeting ## Managing Multiple Keys You can create multiple API keys for different purposes: | Use Case | Recommended Settings | | ----------------------- | ------------------------------------------- | | Development | Low quota, all models, no IP restriction | | Production | Higher quota, specific models, IP whitelist | | Testing | Limited quota, expiration date set | | Third-party Integration | Minimal quota, specific models only | ## Revoking API Keys To revoke an API key: 1. Go to **API Keys** in your dashboard 2. Find the key you want to revoke 3. Click **Delete** or **Disable** > **Caution** > > Revoking a key is immediate and cannot be undone. Any applications using that key will stop working immediately. Organization API key rows also provide quick actions: | Action | Use | | -------------------- | ------------------------------------------------------ | | **Recent Activity** | Review the selected key's last known activity metadata | | **Edit** | Rename the key or change permission mode | | **Open in Chat** | Open Apertis Chat from the key row | | **Disable / Enable** | Temporarily disable or re-enable a key | | **Delete** | Revoke the key so requests using it are rejected | ## Troubleshooting ### Common Issues | Error | Cause | Solution | | ----------------------- | -------------------------------- | ------------------------------------------- | | `401 Unauthorized` | Invalid or missing API key | Check key is correct and included in header | | `403 Forbidden` | Key doesn't have access to model | Check model restrictions on your key | | `429 Too Many Requests` | Rate limit exceeded | Reduce request frequency or upgrade plan | | `402 Payment Required` | Quota exhausted | Add more quota or enable PAYG fallback | ## Related Topics - [Subscription Plans](/billing/subscription-plans) - Learn about quota and billing - [Rate Limits](/billing/rate-limits) - Understand request limits - [Error Codes](/help/error-codes) - Complete error reference --- Source: https://docs.apertis.ai/billing/subscription-plans # Subscription Plans Apertis offers flexible subscription plans designed to meet different usage needs. Choose the plan that best fits your requirements. ## Available Plans ### Plan Comparison | Feature | Lite | Pro | Max | |---------|------|-----|-----| | Monthly Quota | Basic | Standard | Premium | | Model Access | Lite + Free models | Pro + Lite + Free models | All models | | Priority Support | - | Email | Priority | | PAYG Fallback | Optional | Optional | Optional | | Billing Cycles | Monthly, Quarterly, Yearly | Monthly, Quarterly, Yearly | Monthly, Quarterly, Yearly | ### Billing Cycles All plans are available with three billing cycle options: | Cycle | Discount | Best For | |-------|----------|----------| | **Monthly** | Standard price | Flexibility, testing | | **Quarterly** | ~10% savings | Regular usage | | **Yearly** | ~20% savings | Long-term commitment | ## Quota System ### How Quota Works Each subscription includes a monthly quota that resets at the start of each billing cycle: ``` Cycle Start → Use Quota → Cycle End → Quota Reset ↓ ↓ Full Quota New Cycle Begins ``` ### How Billing Works Every API request consumes a **fixed amount of quota** based on the model's **multiplier rate** — regardless of how many tokens you use: ``` Quota Consumed per Request = Model Rate (fixed multiplier) ``` Each request deducts the model's rate from your monthly allowance — whether the response is 10 tokens or 10,000 tokens. Higher-tier plans offer lower rates, giving you more usage per quota. > **Tip** > > This is different from PAYG (Pay-As-You-Go), which charges per token. Subscription billing is simpler: one request = one fixed quota deduction. ### Free Models Every plan includes a selection of models that consume **no quota** — typically fast, lightweight models suited to high-volume or everyday tasks. Because this lineup changes over time, we don't list specific models here. See the [Subscribe page](https://apertis.ai/subscribe) for the models currently available free on your plan. ### Model Rates by Plan Each model has a different rate depending on your plan. Higher-tier plans offer lower rates, giving you more usage per quota. For the full model list and up-to-date rates, see the [Subscribe page](https://apertis.ai/subscribe). You can also query the current rates programmatically using the [Models API](/api/utilities/models) with your subscription key — the response includes a `multiplier` field for each model. > **Tip: Coding Tools** > > When using coding tools such as Claude Code, Roo Code, Cline, Kilo Code, OpenCode, Crush, or Goose, add the `code:` prefix to Claude model IDs (e.g., `code:claude-opus-4-8`). This routes requests through optimized coding channels. The same subscription quota and rates apply. See the [Claude Code guide](/installation/claude-code#coding-model-ids) for details. ### Estimated Uses per Month You can estimate how many requests you get per month by dividing your plan's quota by the model rate: ``` Estimated Uses = Monthly Quota ÷ Model Rate ``` > **Note** > > In practice, most users mix models — using cheaper models for simple tasks and premium models for complex ones. ## PAYG (Pay-As-You-Go) Fallback ### What is PAYG Fallback? When your subscription quota is exhausted, PAYG fallback allows you to continue using the API by charging to your account balance: ``` Subscription Quota Exhausted → PAYG Kicks In → Continue Using API ``` ### Configuring PAYG | Setting | Description | |---------|-------------| | **Enable/Disable** | Toggle PAYG fallback on or off | | **Spending Limit** | Maximum PAYG spending per cycle | | **Current Spent** | PAYG amount used this cycle | ### PAYG Spending Limits Set a spending limit to control costs: ``` Example: Spending Limit = $50/month If subscription quota runs out: - PAYG activates - Maximum $50 additional spending allowed - After $50, requests will be rejected until next cycle ``` ## Subscription Lifecycle ### Subscription Status | Status | Description | |--------|-------------| | **Active** | Subscription is current and usable | | **Suspended** | Temporarily paused (payment issue or manual) | | **Cancelled** | Will end at period end | | **Expired** | Subscription has ended | ### Cycle Reset At the start of each billing cycle: 1. Quota is reset to your plan's limit 2. PAYG spending counter resets to zero 3. Usage history is preserved for reporting ### Payment Failure Handling If a payment fails: 1. **Grace Period**: 3-7 days to resolve payment issue 2. **Suspension**: Subscription suspended if not resolved 3. **Recovery**: Automatic reactivation upon successful payment ## Managing Your Subscription ### Viewing Subscription Status Access your subscription details via **Settings** → **Subscription** tab ([direct link](https://apertis.ai/setting?tab=subscription)): - Current plan and billing cycle - Quota usage (used / limit) - Next billing date - Payment history > **Tip** > > After subscribing, the navbar **Subscribe** button changes to **My Plan** — click it to jump directly to your subscription settings. You can also find **My Plan** in the user dropdown menu. ### Changing Plans You can upgrade or downgrade your plan at any time: **Upgrading:** - Takes effect immediately - Quota is prorated for the remaining period - Additional quota added instantly **Downgrading:** - Takes effect at next billing cycle - Current quota remains until cycle ends ### Cancelling Subscription To cancel your subscription: 1. Go to **Settings** → **Subscription** tab (or click **My Plan** in the navbar) 2. Click **Cancel Subscription** 3. Choose cancellation timing: - **Immediate**: Ends now (no refund for remaining period) - **End of Period**: Continues until current cycle ends > **Note** > > After cancellation, your subscription API key will stop working at the end of the billing period. ## Subscription vs. PAYG-Only ### When to Use Subscription - **Predictable usage**: You know your monthly needs - **Cost savings**: Subscriptions offer better rates than pure PAYG - **Budget planning**: Fixed monthly costs - **Consistent access**: No need to top up balance ### When to Use PAYG-Only - **Variable usage**: Usage fluctuates significantly - **Testing phase**: Evaluating the platform - **Low volume**: Occasional API calls - **No commitment**: Flexibility over savings ### Auto Top-up Enable automatic balance top-up to ensure uninterrupted service: | Setting | Description | |---------|-------------| | **Enable** | Turn on auto top-up | | **Threshold** | Balance level that triggers top-up | | **Amount** | Amount to add when triggered | | **Payment Method** | Card to charge | ``` Example: Threshold: $10 Amount: $50 When balance drops below $10 → Automatically charge $50 to your card ``` ## Dedicated Subscription Token Each subscription comes with a dedicated API token: - **Format**: `sk-sub-xxxxxxxxxxxx` - **Auto-sync**: Quota automatically synced with subscription - **Cycle Reset**: Token quota resets with billing cycle - **Separate from Regular Tokens**: Managed independently ### Finding Your Subscription Token After subscribing, find your dedicated API key in **Settings** → **API Keys** tab ([direct link](https://apertis.ai/setting?tab=keys)): 1. Click **My Plan** in the navbar (or go to **Settings**) 2. Switch to the **API Keys** tab 3. Your subscription key (`sk-sub-...`) is listed with integration guides ### Using Your Subscription Token ```python from openai import OpenAI client = OpenAI( api_key="sk-sub-your-subscription-key", base_url="https://api.apertis.ai/v1" ) # Quota is tracked against your subscription response = client.chat.completions.create( model="claude-sonnet-4-6", messages=[{"role": "user", "content": "Hello!"}] ) ``` ### Viewing Available Models Use your subscription token to query which models are available in your plan: ```python # List all models in your plan models = client.models.list() for model in models: print(model.id) ``` The `/v1/models` endpoint returns only models included in your current plan, along with extra fields like `multiplier` (quota cost) and `tier` (model origin). See [Models API](/api/utilities/models) for details. ## FAQ ### What happens when my quota runs out? - **With PAYG enabled**: API continues working, charged to your balance - **Without PAYG**: API returns `402 Payment Required` error ### Can I change my billing cycle? Yes, you can switch between monthly, quarterly, and yearly at renewal time. ### Is there a free trial? Contact support for trial options and promotional offers. ### Can I get a refund? Refunds are handled on a case-by-case basis. Contact support for assistance. ## Related Topics - [API Keys](/authentication/api-keys) - Manage your API keys - [Rate Limits](/billing/rate-limits) - Understand request limits - [Troubleshooting](/help/troubleshooting) - Common issues and solutions --- Source: https://docs.apertis.ai/billing/quota-management # Quota Management Understanding how quota works is essential for managing your API usage and costs effectively. This guide explains the quota system in detail. ## Quota Overview Quota represents your available API usage credits. Every API request consumes quota based on token usage and model pricing. ``` Request → Token Count → Model Multiplier → Quota Consumed ``` ## Types of Quota ### 1. API Key Quota Each API key has its own quota allocation: | Setting | Description | |---------|-------------| | **Remaining Quota** | Available credits for this key | | **Used Quota** | Total consumed by this key | | **Unlimited** | No quota limit (uses account balance) | ### 2. Subscription Quota Subscription plans include monthly quota that resets each billing cycle: | Component | Description | |-----------|-------------| | **Cycle Limit** | Total quota for current cycle | | **Cycle Used** | Quota consumed this cycle | | **Reset Date** | When quota resets to full | ### 3. Account Balance Your account balance for Pay-As-You-Go (PAYG) usage: | Component | Description | |-----------|-------------| | **Balance** | Current available funds | | **Used** | Total spent on PAYG | ## Quota Calculation ### Subscription Quota — Per-Request Billing Subscription plans use a **per-request** billing model. Each API request consumes a fixed amount of quota based on the model's multiplier rate: ``` Quota Consumed per Request = Model Rate (fixed multiplier) ``` This is independent of token usage — a short response and a long response cost the same quota for the same model. Different plans offer different rates for the same model — higher-tier plans have lower multipliers, giving you more requests per quota. ### PAYG — Per-Token Billing PAYG (Pay-As-You-Go) uses a **per-token** billing model. Each request is charged based on actual token consumption: ``` Cost = (Input Tokens × Input Rate) + (Output Tokens × Output Rate) ``` ### Model Pricing For current model rates and pricing, visit the [Subscribe page](https://apertis.ai/subscribe). ## Monitoring Quota Usage ### Dashboard Overview View your quota status in the dashboard: 1. **Account Overview** - Total balance and usage 2. **API Keys** - Per-key quota status 3. **Subscription** - Cycle quota remaining 4. **Usage History** - Detailed consumption logs ### API Endpoints Check quota programmatically: ```python import requests response = requests.get( "https://api.apertis.ai/v1/dashboard/billing/credits", headers={"Authorization": "Bearer sk-your-api-key"} ) credits = response.json() payg = credits["payg"] if payg["token_is_unlimited"]: print("Token quota: unlimited") else: print(f"Remaining token credits: ${payg['token_remaining']:.2f}") print(f"Used token credits: ${payg['token_used']:.2f}") if credits["is_subscriber"]: subscription = credits["subscription"] print(f"Cycle quota remaining: {subscription['cycle_quota_remaining']}") ``` ### Response Headers API responses include usage information: ```json { "usage": { "prompt_tokens": 50, "completion_tokens": 100, "total_tokens": 150 } } ``` ## Managing Quota Effectively ### 1. Choose the Right Model Match model capabilities to your needs: | Task | Recommended Model | Cost Level | |------|-------------------|------------| | Simple Q&A | GPT-5.4 nano | $ | | Code generation | GPT-5.5 | $$ | | Complex reasoning | Claude Opus 4.8 | $$$ | | Long documents | Claude Sonnet 4.6 | $$ | ### 2. Optimize Prompts Reduce token usage with efficient prompts: ```python # Inefficient (high token usage) prompt = """ I would like you to please help me with the following task. I need you to summarize the following text for me. Please make sure the summary is comprehensive and detailed. Here is the text: {long_text} """ # Efficient (lower token usage) prompt = f"Summarize:\n{long_text}" ``` ### 3. Use System Messages Wisely System messages persist across turns. Keep them concise: ```python # Good - concise system message messages = [ {"role": "system", "content": "You are a helpful coding assistant."}, {"role": "user", "content": "..."} ] # Avoid - overly detailed system message messages = [ {"role": "system", "content": "You are an extremely helpful assistant..."}, # 500+ tokens {"role": "user", "content": "..."} ] ``` ### 4. Implement Caching Cache responses for identical queries: ```python import hashlib from functools import lru_cache @lru_cache(maxsize=1000) def get_cached_response(prompt_hash, model): return client.chat.completions.create( model=model, messages=[{"role": "user", "content": prompt}] ) # Create hash for caching prompt_hash = hashlib.md5(prompt.encode()).hexdigest() response = get_cached_response(prompt_hash, "gpt-5.5") ``` ### 5. Set Quota Limits Configure API key quota limits to prevent overspending: 1. Go to **API Keys** in dashboard 2. Edit the key 3. Set **Quota Limit** 4. Save changes ## Quota Alerts ### Setting Up Alerts Configure alerts to be notified when quota is low: 1. Navigate to **Settings** → **Notifications** 2. Enable **Low Quota Warning** 3. Set threshold percentage (e.g., 20%) 4. Choose notification method (email) ### Alert Thresholds | Level | Threshold | Action | |-------|-----------|--------| | Warning | 20% remaining | Consider top-up | | Critical | 10% remaining | Top-up soon | | Exhausted | 0% remaining | Requests will fail | ## Quota Exhaustion ### What Happens When quota is exhausted: 1. **API Key Quota**: Requests return `402 Payment Required` 2. **Subscription Quota**: Falls back to PAYG per-token billing (if enabled), otherwise returns `402` 3. **Account Balance**: All requests fail with `402` ### Error Response ```json { "error": { "message": "You have exceeded your quota", "type": "billing_error", "code": "quota_exceeded" } } ``` ### Recovery Options | Situation | Solution | |-----------|----------| | API key exhausted | Increase key quota or create new key | | Subscription exhausted | Enable PAYG fallback | | Balance exhausted | Add funds to account | ## Quota Reset ### Subscription Quota Reset Subscription quota resets automatically at the start of each billing cycle: ``` Cycle Start (e.g., Jan 1) → Full Quota Available ↓ Use Throughout Month ↓ Cycle End (e.g., Jan 31) → Unused Quota Expires ↓ Next Cycle (Feb 1) → Full Quota Restored ``` > **Note** > > Unused quota does not roll over to the next cycle. ### Manual Quota Reset (Admin) Account administrators can reset quota for: - Individual API keys - Subscription cycles - User accounts ## Best Practices ### For Development 1. **Use separate keys** for development and production 2. **Set low limits** on development keys 3. **Monitor usage** during testing 4. **Use cheaper models** for development ### For Production 1. **Set appropriate limits** based on expected usage 2. **Enable auto top-up** to prevent service interruption 3. **Monitor alerts** and respond quickly 4. **Review usage regularly** for optimization opportunities ### Cost Control Strategies | Strategy | Implementation | |----------|----------------| | Model tiering | Use cheaper models for simple tasks | | Prompt optimization | Reduce token count in prompts | | Response limits | Set `max_tokens` to limit output | | Caching | Cache frequent identical queries | | Rate limiting | Implement client-side rate limits | ## Usage Reports ### Accessing Reports View detailed usage reports in the dashboard: 1. Go to **Usage** → **Reports** 2. Select date range 3. Filter by model, key, or endpoint 4. Export as CSV if needed ### Report Metrics | Metric | Description | |--------|-------------| | Total Requests | Number of API calls | | Total Tokens | Tokens consumed | | Total Cost | Quota/money spent | | Average Latency | Response time | | Error Rate | Failed requests percentage | ## Related Topics - [Subscription Plans](/billing/subscription-plans) - Plan quotas and features - [Rate Limits](/billing/rate-limits) - Request rate limits - [PAYG](/billing/payg) - Pay-As-You-Go billing - [API Keys](/authentication/api-keys) - Key management --- Source: https://docs.apertis.ai/billing/payg # Pay-As-You-Go (PAYG) Pay-As-You-Go allows you to use the Apertis API without a subscription by paying for what you use. This guide explains how PAYG works and how to manage it effectively. ## Overview PAYG is a flexible billing model where you: 1. **Add funds** to your account balance 2. **Use the API** as needed 3. **Pay per usage** based on actual consumption ``` Add Funds → Make Requests → Deduct from Balance → Top Up When Low ``` ## PAYG vs. Subscription | Feature | PAYG | Subscription | |---------|------|--------------| | Commitment | None | Monthly/Yearly | | Pricing | Standard rates | Discounted rates | | Quota Reset | Never (balance-based) | Monthly cycle | | Best For | Variable usage | Predictable usage | | Minimum | $5 | Plan price | ### When to Use PAYG - **Testing phase** - Evaluating the platform - **Sporadic usage** - Occasional API calls - **Unpredictable volume** - Usage varies significantly - **Budget flexibility** - Pay only for what you use ### When to Use Subscription - **Regular usage** - Consistent monthly volume - **Cost savings** - Lower per-token rates - **Budgeting** - Fixed monthly costs - **High volume** - Heavy API usage ## Adding Funds ### Payment Methods | Method | Availability | Processing | |--------|--------------|------------| | Credit/Debit Card | Global | Instant | All payments are securely processed through **Stripe**. ### Top-Up Process 1. Log in to [Dashboard](https://apertis.ai/login) 2. Go to **Billing** → **Add Funds** 3. Select amount 4. Choose payment method 5. Complete payment ### Minimum Top-Up | Method | Minimum | |--------|---------| | Credit Card | $5 | ## PAYG Pricing PAYG uses standard pricing per token based on the model used. For current model pricing, please visit the [Models page](https://apertis.ai/models). ### Cost Calculation ``` Cost = (Input Tokens × Input Rate) + (Output Tokens × Output Rate) ``` ## PAYG Fallback for Subscriptions Subscription users can enable PAYG as a fallback when subscription quota is exhausted. ### How It Works ``` Request → Check Subscription Quota ↓ Quota Available? → Yes → Deduct Model Rate from Quota (per-request) ↓ No PAYG Enabled? → Yes → Charge Entire Request per Token (from balance) ↓ No Return 402 Error ``` > **Note** > > When PAYG fallback activates, the **entire request** is billed using PAYG per-token pricing. There is no partial split between subscription quota and PAYG — it's one or the other. ### Enabling PAYG Fallback 1. Go to **Billing** → **Subscription** 2. Find **PAYG Fallback** section 3. Toggle **Enable PAYG Fallback** 4. Set **Spending Limit** (optional) 5. Save changes ### Spending Limits Set a maximum PAYG spending per billing cycle: | Setting | Effect | |---------|--------| | No limit | Use balance until exhausted | | $50/cycle | Max $50 PAYG per month | | $0 | PAYG disabled (same as off) | ### Billing Mode Switch Subscription and PAYG use different billing models: | Billing Mode | How It Charges | |-------------|----------------| | **Subscription** | Fixed quota per request (model rate × 1) | | **PAYG** | Per token (input tokens × input rate + output tokens × output rate) | When your subscription quota runs out and PAYG is enabled, the next request is charged entirely through PAYG per-token pricing. There is no partial split between the two billing modes. ## Auto Top-Up Automatically add funds when balance drops below a threshold. ### Configuration | Setting | Description | |---------|-------------| | **Enable** | Turn on auto top-up | | **Threshold** | Balance level to trigger | | **Amount** | Amount to add each time | | **Payment Method** | Card to charge | ### Example Setup ``` Threshold: $10 Amount: $50 When balance drops below $10: → Automatically charge $50 to saved card → New balance: current + $50 ``` ### Setting Up Auto Top-Up 1. Go to **Billing** → **Payment Methods** 2. Add or select a payment method 3. Go to **Auto Top-Up** settings 4. Enable and configure threshold/amount 5. Save ### Best Practices | Scenario | Threshold | Amount | |----------|-----------|--------| | Light usage | $5 | $20 | | Regular usage | $20 | $50 | | Heavy usage | $50 | $100 | | Production | $100 | $200 | ## Monitoring PAYG Usage ### Dashboard Views - **Balance Overview** - Current balance and recent transactions - **Usage Graph** - Daily/weekly/monthly spending trends - **Transaction History** - Detailed transaction log ### API Access Check balance programmatically: ```python import requests response = requests.get( "https://api.apertis.ai/v1/dashboard/billing/credits", headers={"Authorization": "Bearer sk-your-api-key"} ) credits = response.json() balance = credits["payg"]["account_credits"] print(f"Current balance: ${balance:.2f} USD") ``` ## Low Balance Handling ### Warning Levels | Balance | Status | Recommendation | |---------|--------|----------------| | > $20 | Normal | Continue usage | | $10-20 | Low | Consider top-up | | $5-10 | Warning | Top up soon | | < $5 | Critical | Top up immediately | ### When Balance Exhausted ```json { "error": { "message": "Insufficient account balance", "type": "billing_error", "code": "insufficient_balance" } } ``` ### Recovery Steps 1. **Immediate**: Add funds via dashboard 2. **Prevention**: Enable auto top-up 3. **Monitoring**: Set up low balance alerts ## PAYG for Teams ### Shared Balance Team accounts share a common balance: - All team members use the same balance - Usage is tracked per API key - Billing goes to account owner ### Per-Key Limits Control spending per team member: 1. Create separate API keys for each member 2. Set quota limits on each key 3. Monitor usage per key ## Cost Optimization ### Model Selection Choose cost-effective models for your use case: | Use Case | Recommended | Cost | |----------|-------------|------| | Simple chat | GPT-5.4 nano | $ | | General tasks | GPT-5.4 mini | $ | | Complex reasoning | GPT-5.5 | $$ | | Long context | Claude Sonnet 4.6 | $$ | ### Prompt Optimization Reduce costs by optimizing prompts: ```python # Expensive: Long, verbose prompt prompt = """ I would like you to help me with the following task. Please read the text below carefully and provide a detailed summary of the main points... """ # Cheaper: Concise prompt prompt = "Summarize the key points:" ``` ### Caching Strategy Implement caching for repeated queries: ```python import hashlib from functools import lru_cache @lru_cache(maxsize=1000) def cached_completion(prompt_hash): # Only calls API if not cached return client.chat.completions.create(...) ``` ### Set Max Tokens Limit response length to control costs: ```python response = client.chat.completions.create( model="gpt-5.5", messages=[...], max_tokens=500 # Limit output length ) ``` ## Billing Statements ### Viewing Statements 1. Go to **Billing** → **Statements** 2. Select month 3. View or download PDF ### Statement Contents - Beginning balance - Top-ups during period - Usage breakdown by model - Ending balance - Transaction details ## Refunds ### Refund Policy - Unused balance is generally non-refundable - Contact support for exceptional circumstances - Refunds processed to original payment method ### Requesting a Refund 1. Contact support at hi@apertis.ai 2. Provide account email and reason 3. Allow 5-7 business days for review ## FAQ ### Can I switch from PAYG to subscription? Yes, you can subscribe at any time. Your PAYG balance remains available for fallback usage. ### What happens to unused PAYG balance? Balance never expires and remains available until used. ### Can I get invoices for PAYG purchases? Yes, invoices are available in the Billing section for all top-ups. ### Is there a minimum balance required? No minimum balance is required, but we recommend maintaining at least $5 for uninterrupted service. ## Related Topics - [Subscription Plans](/billing/subscription-plans) - Compare with subscriptions - [Quota Management](/billing/quota-management) - Understanding quota - [Rate Limits](/billing/rate-limits) - Request limits - [API Keys](/authentication/api-keys) - Key management --- Source: https://docs.apertis.ai/billing/payment-methods # Payment Methods Apertis supports multiple payment methods to accommodate users worldwide. This guide explains the available options and how to manage them. ## Supported Payment Methods | Method | Availability | Currencies | Processing Time | |--------|--------------|------------|-----------------| | Credit/Debit Card | Global | USD | Instant | All payments are securely processed through **Stripe**. ## Credit & Debit Cards ### Supported Cards We accept major credit and debit cards through Stripe: - Visa - Mastercard - American Express - Discover - JCB - UnionPay ### Adding a Card 1. Go to **Billing** → **Payment Methods** 2. Click **Add Payment Method** 3. Enter card details: - Card number - Expiration date - CVC/CVV - Billing address 4. Click **Save** ### Card Security All card transactions are processed through Stripe, a PCI DSS Level 1 certified payment processor. We never store your full card number. ### Default Payment Method Set a default payment method for: - Subscription renewals - Auto top-up charges - Future purchases **To set default:** 1. Go to **Payment Methods** 2. Find the card 3. Click **Set as Default** ### Removing a Card 1. Go to **Payment Methods** 2. Find the card to remove 3. Click **Remove** > **Caution** > > You cannot remove a card if it's: > - The only payment method for an active subscription > - Set for auto top-up ### Card Declined If your card is declined: | Reason | Solution | |--------|----------| | Insufficient funds | Ensure adequate balance | | Card expired | Update with new card | | Incorrect details | Verify card information | | Bank block | Contact your bank to allow the transaction | | International block | Enable international transactions | ## Auto Top-Up ### What is Auto Top-Up? Automatically add funds when your balance drops below a threshold. Ensures uninterrupted API access. ### Setting Up Auto Top-Up 1. Go to **Billing** → **Auto Top-Up** 2. Toggle **Enable Auto Top-Up** 3. Set **Threshold** (e.g., $10) 4. Set **Top-Up Amount** (e.g., $50) 5. Select **Payment Method** 6. Save settings ### How It Works ``` Balance drops below $10 (threshold) ↓ System charges $50 to your card ↓ Balance increases by $50 ↓ Continue using API without interruption ``` ### Configuration Options | Setting | Description | Recommended | |---------|-------------|-------------| | Threshold | Balance level to trigger | $10-$50 | | Amount | Amount to add each time | $20-$100 | | Payment Method | Card to charge | Default card | ### Auto Top-Up Best Practices | Usage Level | Threshold | Amount | |-------------|-----------|--------| | Light | $5 | $20 | | Moderate | $20 | $50 | | Heavy | $50 | $100 | | Enterprise | $100+ | $500+ | ### Disabling Auto Top-Up 1. Go to **Billing** → **Auto Top-Up** 2. Toggle **Off** 3. Save changes ## Invoices & Receipts ### Accessing Invoices 1. Go to **Billing** → **Invoices** 2. Select the invoice 3. Download PDF ### Invoice Contents Each invoice includes: - Invoice number - Date - Amount and currency - Payment method - Usage summary (optional) - Tax information (if applicable) ### Tax Information To add tax ID or company information to invoices: 1. Go to **Account Settings** → **Billing Information** 2. Enter: - Company name - Tax ID / VAT number - Billing address 3. Save changes This information appears on all future invoices. ### Receipts Receipts are generated automatically for each payment: 1. Go to **Billing** → **Payment History** 2. Find the transaction 3. Click **Receipt** ## Payment Troubleshooting ### Payment Failed Common causes and solutions: | Cause | Solution | |-------|----------| | Insufficient funds | Add funds to your bank account | | Card expired | Update payment method | | Fraud protection | Contact bank to allow | | Incorrect CVV | Re-enter card details | | Billing address mismatch | Update address to match bank records | ### Duplicate Charges If you see duplicate charges: 1. Wait 24 hours (temporary holds may clear) 2. Check if both actually completed 3. Contact support with transaction details ### Refund Requests For refund requests: 1. Email hi@apertis.ai 2. Include: - Account email - Transaction date - Amount - Reason for refund Processing time: 5-10 business days ## Regional Payment Options ### Global - All major credit/debit cards (Visa, Mastercard, American Express, JCB, UnionPay) - Processed securely through Stripe ## Enterprise Payment Options For enterprise customers with specific requirements: - Wire transfer - Purchase orders - Custom billing cycles - Volume discounts Contact sales: hi@apertis.ai ## Security Best Practices ### Protecting Payment Information 1. **Use strong account password** 2. **Enable 2FA** on your account 3. **Monitor transactions** regularly 4. **Set spending limits** on API keys 5. **Review invoices** monthly ### Fraud Prevention We implement multiple fraud prevention measures: - 3D Secure for card payments - Address verification (AVS) - CVV verification - Transaction monitoring - Suspicious activity alerts ## FAQ ### Can I pay annually? Yes, subscription plans offer monthly, quarterly, and yearly billing cycles. Annual billing typically offers discounts. ### Is my payment information secure? Yes. All payments are processed by Stripe, a PCI DSS Level 1 certified payment processor. We never store full card numbers. ### Can I get a paid receipt? Yes, invoices and receipts are available in the Billing section for all transactions. ### What currency are charges in? All charges are in USD, with local currency conversion handled by your bank or payment processor. ### Why was my payment declined? Common reasons include insufficient funds, expired card, bank security blocks, or incorrect billing information. Contact your bank for specific details. ## Related Topics - [Subscription Plans](/billing/subscription-plans) - Plan options - [Quota Management](/billing/quota-management) - Managing usage - [PAYG](/billing/payg) - Pay-as-you-go billing - [FAQ](/help/faq) - Common questions --- Source: https://docs.apertis.ai/billing/rate-limits # Rate Limits Apertis implements rate limits to ensure fair usage and maintain service quality for all users. This guide explains the different rate limits and how to handle them. ## Rate Limit Overview Rate limits are applied at multiple levels to protect the service: | Level | Limit | Duration | Scope | |-------|-------|----------|-------| | **Global API** | 1,500 requests | 3 minutes | Per IP | | **API Key** | 3,000 requests | 1 minute | Per Key | | **Per-Model** | Varies by plan | 1 minute | Per Key + Per Model | | **Web Dashboard** | 120 requests | 3 minutes | Per IP | | **Critical Operations** | 20 requests | 20 minutes | Per IP | | **Log Queries** | 30 requests | 60 seconds | Per User | ## API Rate Limits ### Global API Rate Limit The primary rate limit for all API endpoints: ``` 1,500 requests per 3 minutes per IP address ``` This applies to: - `/v1/chat/completions` - `/v1/embeddings` - `/v1/images/generations` - All other `/v1/*` endpoints ### Per API Key Rate Limit Each API key has its own rate limit: ``` 3,000 requests per 1 minute per API key ``` This provides higher throughput for applications using a single API key. ### Per-Model Rate Limit Each API key is also rate-limited **per model**. This prevents a single key from overwhelming any specific model with rapid requests (e.g., automated agent loops). Model name variants are unified into a single bucket — for example, `claude-opus-4-8` and `code:claude-opus-4-8` share the same rate limit counter. The `:web` and `:free` suffixes are also normalized. | Plan | RPM per Model per Key | |------|----------------------| | **Lite** ($12/mo) | 15 | | **Pro** ($25/mo) | 20 | | **Max** ($200/mo) | 30 | | **PAYG** | 500 | > **Tip** > > Requests that are rate-limited return HTTP 429 immediately and **do not consume any quota or credits**. The request never reaches the upstream provider. ### Effective Rate Limit Your effective rate limit is the **lowest** of: - Global API limit (per IP) - API Key limit (per key) - Per-Model limit (per key + per model, based on your plan) ``` Effective Limit = min(Global Limit, API Key Limit, Per-Model Limit) ``` ## Rate Limit Headers Rate limit information is included in API response headers: | Header | Description | |--------|-------------| | `X-RateLimit-Limit` | Maximum requests allowed | | `X-RateLimit-Remaining` | Requests remaining in window | | `X-RateLimit-Reset` | Unix timestamp when limit resets | ### Example Response Headers ```http HTTP/1.1 200 OK X-RateLimit-Limit: 1500 X-RateLimit-Remaining: 1423 X-RateLimit-Reset: 1703894400 ``` ## Handling Rate Limits ### HTTP 429 Response When you exceed the rate limit, the API returns HTTP 429 with a `Retry-After` header indicating how many seconds to wait: ```http HTTP/1.1 429 Too Many Requests Retry-After: 45 ``` ```json { "error": { "message": "Rate limit exceeded for model: claude-opus-4-8. Maximum 20 requests per minute per API key. Please wait before retrying.", "type": "rate_limit_error", "code": "rate_limit_exceeded" } } ``` ### Retry Strategy Implement exponential backoff for rate limit errors: ```python import time import random from openai import OpenAI, RateLimitError client = OpenAI( api_key="sk-your-api-key", base_url="https://api.apertis.ai/v1" ) def make_request_with_retry(messages, max_retries=5): for attempt in range(max_retries): try: return client.chat.completions.create( model="gpt-5.5", messages=messages ) except RateLimitError: if attempt == max_retries - 1: raise # Exponential backoff with jitter wait_time = (2 ** attempt) + random.uniform(0, 1) print(f"Rate limited. Waiting {wait_time:.2f}s...") time.sleep(wait_time) ``` ### Node.js Example ```javascript import OpenAI from 'openai'; const client = new OpenAI({ apiKey: 'sk-your-api-key', baseURL: 'https://api.apertis.ai/v1' }); async function makeRequestWithRetry(messages, maxRetries = 5) { for (let attempt = 0; attempt < maxRetries; attempt++) { try { return await client.chat.completions.create({ model: 'gpt-5.5', messages }); } catch (error) { if (error.status !== 429 || attempt === maxRetries - 1) { throw error; } // Exponential backoff with jitter const waitTime = Math.pow(2, attempt) + Math.random(); console.log(`Rate limited. Waiting ${waitTime.toFixed(2)}s...`); await new Promise(r => setTimeout(r, waitTime * 1000)); } } } ``` ## Best Practices ### 1. Request Batching Combine multiple operations into fewer requests: ```python # Instead of multiple single-message calls for message in messages: client.chat.completions.create(messages=[message]) # Many requests # Batch into single calls when possible client.chat.completions.create(messages=messages) # Single request ``` ### 2. Implement Request Queuing Use a queue to manage request rate: ```python import asyncio from collections import deque class RateLimitedQueue: def __init__(self, max_requests_per_minute=50): self.max_rpm = max_requests_per_minute self.queue = deque() self.request_times = deque() async def add_request(self, request_func): # Wait if at rate limit while len(self.request_times) >= self.max_rpm: oldest = self.request_times[0] wait_time = 60 - (time.time() - oldest) if wait_time > 0: await asyncio.sleep(wait_time) self.request_times.popleft() # Execute request self.request_times.append(time.time()) return await request_func() ``` ### 3. Use Streaming for Long Responses Streaming doesn't reduce rate limits but improves perceived performance: ```python response = client.chat.completions.create( model="gpt-5.5", messages=[{"role": "user", "content": "Write a long story"}], stream=True ) for chunk in response: print(chunk.choices[0].delta.content, end="") ``` ### 4. Cache Responses Cache identical requests to reduce API calls: ```python import hashlib from functools import lru_cache @lru_cache(maxsize=1000) def cached_completion(messages_hash, model): # Only call API if not in cache return client.chat.completions.create( model=model, messages=messages # Original messages ) # Create hash of messages for cache key messages_hash = hashlib.md5(str(messages).encode()).hexdigest() response = cached_completion(messages_hash, "gpt-5.5") ``` ### 5. Distribute Across Multiple Keys For high-volume applications, use multiple API keys: ```python import itertools api_keys = ["sk-key1", "sk-key2", "sk-key3"] key_cycle = itertools.cycle(api_keys) def get_client(): return OpenAI( api_key=next(key_cycle), base_url="https://api.apertis.ai/v1" ) ``` ## Special Rate Limits ### Critical Operations Higher restrictions for security-sensitive operations: | Operation | Limit | Duration | |-----------|-------|----------| | Registration | 20 | 20 minutes | | Password Reset | 20 | 20 minutes | | Email Verification | 20 | 20 minutes | ### Log Query Rate Limit For log and usage queries: ``` 30 requests per 60 seconds per user ``` > **Note** > > Root users (administrators) are exempt from log query rate limits. ### Upload/Download Rate Limit For file operations: ``` 10 requests per 60 seconds per IP ``` ## Rate Limits by Plan ### Subscription Users Subscription users have per-model RPM limits that vary by plan tier. See the [Per-Model Rate Limit](#per-model-rate-limit) section above for details. Global API (1,500/3min) and API Key (3,000/min) limits remain the same across all plans. ### PAYG Users PAYG users have a generous per-model RPM limit of 500 requests per minute, since usage is billed directly. ### Enterprise Contact sales for custom rate limits for enterprise applications. ## Monitoring Your Usage ### Dashboard Metrics View your API usage in the dashboard: - Requests per minute/hour/day - Rate limit hits - Peak usage times ### Programmatic Monitoring Track rate limits in your application: ```python def check_rate_limit_headers(response): remaining = response.headers.get('X-RateLimit-Remaining') reset_time = response.headers.get('X-RateLimit-Reset') if remaining and int(remaining) < 100: print(f"Warning: Only {remaining} requests remaining") print(f"Resets at: {reset_time}") ``` ## Troubleshooting ### Consistently Hitting Rate Limits If you're frequently rate limited: 1. **Audit your request patterns** - Are you making unnecessary calls? 2. **Implement caching** - Cache repeated queries 3. **Optimize batch sizes** - Combine requests where possible 4. **Consider upgrading** - Higher plans have increased limits ### Sudden Rate Limit Errors If you suddenly start getting rate limited: 1. **Check for loops** - Infinite loops can exhaust limits quickly 2. **Review recent code changes** - New code may have introduced issues 3. **Check for concurrent users** - Multiple processes sharing same key ## Related Topics - [API Keys](/authentication/api-keys) - Manage API keys and access - [Subscription Plans](/billing/subscription-plans) - Plan limits and quotas - [Error Codes](/help/error-codes) - Understanding error responses --- Source: https://docs.apertis.ai/installation/claude-code # Claude Code Claude Code is an agentic terminal tool that understands codebases and accelerates development by automating routine tasks, analyzing complex code, and managing git workflows through natural language commands. ## Prerequisites - Node.js 18 or newer installed on your system (for npm installation) - An Apertis API Key (obtain from [Apertis Dashboard](https://apertis.ai/setting?tab=keys)) ## Installation ### Native Installer (Recommended) **macOS / Linux / WSL:** ```bash curl -fsSL https://claude.ai/install.sh | bash ``` **Windows PowerShell:** ```powershell irm https://claude.ai/install.ps1 | iex ``` ### npm Installation Alternatively, install via npm with Node.js 18+: ```bash npm install -g @anthropic-ai/claude-code ``` ## Configuration Configure Claude Code to connect to Apertis API using the switch script below. You only need to modify **two values**: your API key and your preferred default model. ### Setup Create a file named `ccswitch.sh` in your home directory (or any location you prefer): ```bash #!/usr/bin/env bash # ─── User Configuration ──────────────────────────────────────── # Replace the values below with your own credentials and preferences. APERTIS_BASE_URL="https://api.apertis.ai" APERTIS_AUTH_TOKEN="your_apertis_api_key" # ← Your API key from https://apertis.ai/setting?tab=keys DEFAULT_MODEL="code:claude-opus-4-8" # ← Default model when using --apertis # ─── Script Logic (no changes needed below) ──────────────────── usage() { cat < Switch to Apertis with a specific model EOF } export_apertis() { local model="$1" export ANTHROPIC_BASE_URL="$APERTIS_BASE_URL" export ANTHROPIC_AUTH_TOKEN="$APERTIS_AUTH_TOKEN" export ANTHROPIC_MODEL="$model" echo "✅ Apertis enabled" echo " MODEL=$ANTHROPIC_MODEL" } unset_all() { unset ANTHROPIC_BASE_URL unset ANTHROPIC_AUTH_TOKEN unset ANTHROPIC_MODEL echo "🧹 Anthropic env unset — reverted to defaults" } if [[ $# -eq 0 ]]; then usage return 1 fi case "$1" in --apertis) export_apertis "$DEFAULT_MODEL" ;; --cc) unset_all ;; --model) if [[ -z "$2" ]]; then echo "❌ Missing model id" usage return 1 fi export_apertis "$2" ;; *) usage return 1 ;; esac ``` ### Activate the Script Add the following alias to your shell profile (`~/.bashrc` or `~/.zshrc`): ```bash alias ccs="source ~/ccswitch.sh" ``` Then reload your shell: ```bash source ~/.zshrc # or source ~/.bashrc ``` > **Warning: Source, not Execute** > > This script **must** be sourced (via `source` or the `ccs` alias), not executed directly (`bash ccswitch.sh`). Direct execution runs in a subprocess, and the exported environment variables will not persist in your current terminal session. ### Commands | Command | Description | |---------|-------------| | `ccs --apertis` | Switch to Apertis API using the `DEFAULT_MODEL` defined in the script | | `ccs --cc` | Unset all Apertis environment variables, reverting to the default Anthropic endpoint | | `ccs --model ` | Switch to Apertis API with a specific model ID | ### Configuration Variables Edit the top of `ccswitch.sh` to match your credentials: | Variable | Description | Example | |----------|-------------|---------| | `APERTIS_BASE_URL` | Apertis API endpoint. Typically no change needed. | `https://api.apertis.ai` | | `APERTIS_AUTH_TOKEN` | Your personal API key from [Apertis Dashboard](https://apertis.ai/setting?tab=keys). | `sk-xxxx...` | | `DEFAULT_MODEL` | The model used when running `ccs --apertis` without specifying a model. Use the `code:` prefix for coding tools. | `code:claude-opus-4-8` | ### Available Model IDs When using `ccs --model`, pass the model ID directly. **Only Claude models require the `code:` prefix**; all other models use their standard ID as-is. **Claude Models** (require `code:` prefix, except Haiku): | Model ID | Description | |----------|-------------| | `code:claude-fable-5` | Claude Fable 5 — newest generation | | `code:claude-sonnet-4-6` | Claude Sonnet 4.6 — balanced speed and capability | | `code:claude-opus-4-8-thinking` | Claude Opus 4.8 with extended thinking | | `code:claude-opus-4-8` | Claude Opus 4.8 — most capable | | `claude-haiku-4-5-thinking` | Claude Haiku 4.5 with extended thinking | | `claude-haiku-4-5` | Claude Haiku 4.5 — fastest, ideal for simple tasks | For more Claude models, see the [Claude Models page](https://apertis.ai/models). **Other Models** (no prefix needed): ```bash ccs --model gpt-5.4 ccs --model gemini-3.1-pro-preview # and more... ``` For the full list of available model IDs, check the [Model List](https://apertis.ai/models). ### Example Usage ```bash # Switch to Apertis with the default model ccs --apertis # ✅ Apertis enabled # MODEL=code:claude-opus-4-8 # Switch to a specific model ccs --model code:claude-sonnet-4-6 # ✅ Apertis enabled # MODEL=code:claude-sonnet-4-6 # Revert to default Anthropic endpoint ccs --cc # 🧹 Anthropic env unset — reverted to defaults ``` ## Launch and Verify Navigate to your project directory and start Claude Code: ```bash cd your-project-directory claude ``` Grant file access permissions when prompted. ### Verify Connection Run the status command inside Claude Code to verify your configuration: ``` /status ``` You should see output similar to: ``` Auth token: ANTHROPIC_AUTH_TOKEN Anthropic base URL: https://api.apertis.ai ``` ## Model Configuration ### Coding Model IDs When using Claude Code (or any coding tool) with Apertis, use the `code:` prefix for Claude model IDs. This routes requests through optimized coding channels: | Standard Model ID | Coding Model ID | |-------------------|-----------------| | `claude-opus-4-8` | `code:claude-opus-4-8` | | `claude-sonnet-4-6` | `code:claude-sonnet-4-6` | > **Tip: Why `code:` prefix?** > > The `code:` prefix routes requests through dedicated coding channels optimized for tool use, extended thinking, and streaming — features essential for coding assistants. Always use `code:` prefixed model IDs with Claude Code and other coding tools. ### Selecting Models in Claude Code You can also switch models directly inside Claude Code using the `/model` command: ``` /model ``` Your configured model ID will appear in the list. Select it to start using a different model. For the latest available models, check the [Model List](https://apertis.ai/models). ## Troubleshooting ### Configuration Not Taking Effect - **Close all Claude Code windows** and open a new terminal - Run `ccs --apertis` again to re-export environment variables - Then run `claude` to start with new settings ### Verify Configuration Inside Claude Code, run `/status` to verify the API endpoint: ``` Auth token: ANTHROPIC_AUTH_TOKEN Anthropic base URL: https://api.apertis.ai ``` ### Connection Issues If you encounter connection errors: 1. Verify your API key is correct at [Apertis Dashboard](https://apertis.ai/setting?tab=keys) 2. Ensure `APERTIS_BASE_URL` in `ccswitch.sh` is `https://api.apertis.ai` 3. Check your network connection and firewall settings ### Tool Use Failures If Claude Code fails to execute file operations or commands: - Ensure your selected model supports tool use capabilities - All Claude models available through Apertis support tool use - Check that you have granted the necessary file access permissions ## Features Claude Code provides powerful capabilities including: - **Code Understanding**: Analyzes your entire codebase context - **Automated Refactoring**: Helps refactor and improve code quality - **Git Workflow**: Manages commits, branches, and pull requests - **Bug Fixing**: Identifies and helps fix issues in your code - **Documentation**: Generates documentation and comments - **Testing**: Helps write and run tests ## Related Resources - [Apertis API Documentation](https://docs.apertis.ai) - [Model List](https://apertis.ai/models) - [API Keys Management](https://apertis.ai/setting?tab=keys) - [Claude Code Official Documentation](https://docs.anthropic.com/en/docs/claude-code/configuration) --- Source: https://docs.apertis.ai/installation/opencode # OpenCode OpenCode is an AI coding agent that provides both CLI and IDE integration capabilities for AI-powered development tasks. ## Features - **CLI Access**: Run AI coding tasks directly from terminal - **IDE Integration**: Works with VS Code, Cursor, and compatible editors - **GitHub Integration**: Interact with issues and PRs via comments - **Conversation Sharing**: Share coding sessions with others - **MCP Support**: Enhanced functionality through MCP servers ## Installation ### Method 1: Install Script (Recommended) ```bash curl -fsSL https://opencode.ai/install | bash ``` ### Method 2: NPM ```bash npm install -g opencode-ai ``` ## Configuration for Apertis API Create or edit the configuration file at `~/.config/opencode/opencode.json`: ### Option 1: Using Apertis SDK (Recommended) ```json { "$schema": "https://opencode.ai/config.json", "provider": { "apertis": { "name": "Apertis AI", "npm": "@apertis/ai-sdk-provider", "options": { "apiKey": "your_apertis_api_key" }, "models": { "claude-opus-4-8-thinking": { "name": "Claude Opus 4.8 (Thinking)" }, "claude-opus-4-8": { "name": "Claude Opus 4.8" }, "claude-sonnet-4-6": { "name": "Claude Sonnet 4.6" }, "claude-haiku-4-5": { "name": "Claude Haiku 4.5" }, "gemini-3.1-pro-preview": { "name": "Gemini 3.1 Pro Preview" }, "gemini-3.1-flash-lite-preview": { "name": "Gemini 3.1 Flash Lite Preview" } } } }, "model": "apertis/claude-opus-4-8" } ``` ### Option 2: Without SDK (OpenAI-Compatible) If you prefer not to use the Apertis SDK, you can use the built-in OpenAI-compatible mode by specifying `baseURL`: ```json { "$schema": "https://opencode.ai/config.json", "provider": { "apertis": { "name": "Apertis AI", "options": { "baseURL": "https://api.apertis.ai/v1", "apiKey": "your_apertis_api_key" }, "models": { "claude-opus-4-8": { "name": "Claude Opus 4.8" }, "claude-sonnet-4-6": { "name": "Claude Sonnet 4.6" } } } }, "model": "apertis/claude-opus-4-8" } ``` Replace `your_apertis_api_key` with your actual API key from [Apertis Dashboard](https://apertis.ai/setting?tab=keys). > **Tip: No `code:` prefix needed** > > Unlike Claude Code, OpenCode does **not** require the `code:` prefix for Claude model IDs. Use the standard model ID directly (e.g., `claude-opus-4-8` instead of `code:claude-opus-4-8`). ### Configuration Fields | Field | Description | |-------|-------------| | `provider.apertis.npm` | Apertis SDK package. Use `@apertis/ai-sdk-provider` for automatic setup. | | `provider.apertis.options.baseURL` | Apertis API endpoint. Only needed without the SDK. | | `provider.apertis.options.apiKey` | Your API key from [Apertis Dashboard](https://apertis.ai/setting?tab=keys). | | `provider.apertis.models` | Available models to use. Add or remove as needed. | | `model` | Default model on launch. Format: `apertis/`. | ### Available Model IDs | Model ID | Description | |----------|-------------| | `claude-opus-4-8-thinking` | Claude Opus 4.8 with extended thinking | | `claude-opus-4-8` | Claude Opus 4.8 — most capable | | `claude-sonnet-4-6` | Claude Sonnet 4.6 — balanced speed and capability | | `claude-haiku-4-5` | Claude Haiku 4.5 — fastest | | `gemini-3.1-pro-preview` | Gemini 3.1 Pro Preview | | `gemini-3.1-flash-lite-preview` | Gemini 3.1 Flash Lite Preview | For the full list of available models, check the [Model List](https://apertis.ai/models). ### Launch OpenCode ```bash opencode ``` Use the `/models` command within OpenCode to switch models. ## IDE Integration OpenCode works with VS Code, Cursor, and compatible terminals. ### Keyboard Shortcuts | Action | macOS | Windows/Linux | |--------|-------|---------------| | Quick Launch | `Cmd+Esc` | `Ctrl+Esc` | | New Session | `Cmd+Shift+Esc` | `Ctrl+Shift+Esc` | | File References | `Cmd+Option+K` | `Alt+Ctrl+K` | ## Commands | Command | Description | |---------|-------------| | `/models` | Select AI model | | `/share` | Create public conversation link | | `/unshare` | Remove public access | | `/help` | Show available commands | ## GitHub Integration Interact with OpenCode directly in GitHub issues and pull requests. ### Setup Run this command in your GitHub repository: ```bash opencode github install ``` ### Usage Comment with `/opencode` or `/oc` in GitHub issues/PRs to: - **Explain Issues**: Get AI explanations for bug reports - **Fix Bugs**: Generate fixes for reported issues - **Implement Features**: Create code for feature requests - **Review PRs**: Get AI-powered code reviews ## MCP Server Support OpenCode supports Model Context Protocol servers: - **Vision Server**: Analyze images and screenshots - **Web Search Server**: Search the web for information - **Web Reader Server**: Extract content from web pages ## Troubleshooting ### Connection Issues - Verify your API key at [Apertis Dashboard](https://apertis.ai/setting?tab=keys) - Ensure the Base URL is set correctly - Check environment variables are exported ### Authentication Problems Reset authentication and reconfigure: ```bash opencode auth logout opencode auth login ``` ## Related Resources - [Model List](https://apertis.ai/models) - [API Keys Management](https://apertis.ai/setting?tab=keys) - [Claude Code](/installation/claude-code) - Alternative terminal AI coding tool - [OpenCode Documentation](https://opencode.ai/docs) --- Source: https://docs.apertis.ai/installation/crush # Crush Crush is a terminal-based AI coding agent (CLI + TUI) that supports multiple models for code generation, debugging, and file operations. ## Features - **Code Generation**: Generate code from natural language - **Debugging**: Identify and fix issues in your code - **File Operations**: Create and modify files - **Technical Q&A**: Get answers to programming questions - **TUI Interface**: Interactive terminal user interface ## Installation ### Homebrew (macOS) ```bash brew install charmbracelet/tap/crush ``` ### NPM (Cross-Platform) ```bash npm install -g @charmland/crush ``` ### Arch Linux ```bash yay -S crush-bin ``` ### Nix ```bash nix run github:numtide/nix-ai-tools#crush ``` ## Configuration for Apertis API ### Step 1: Get API Key Obtain your API key from [Apertis Dashboard](https://apertis.ai/setting?tab=keys). ### Step 2: Configure crush.json Create or edit the configuration file: **Location:** - macOS/Linux: `~/.config/crush/crush.json` - Windows: `%USERPROFILE%\.config\crush\crush.json` **Configuration:** ```json { "providers": { "apertis": { "id": "apertis", "name": "Apertis Provider", "base_url": "https://api.apertis.ai/v1", "api_key": "your_apertis_api_key" } } } ``` Replace `your_apertis_api_key` with your actual API key. ### Step 3: Launch Crush ```bash crush ``` ### Step 4: Switch Model Press `Ctrl+P` and select "Switch Model" to choose your preferred model. ## Available Models | Model | Name | |-------|------| | Claude Sonnet 4.6 | `claude-sonnet-4-6` | | Claude Haiku 4.5 | `claude-haiku-4-5` | | GPT-5.5 | `gpt-5.5` | | GPT-5.4 mini | `gpt-5.4-mini` | | Gemini 3.5 Flash | `gemini-3.5-flash` | ## Keyboard Shortcuts | Action | Shortcut | |--------|----------| | Switch Model | `Ctrl+P` | | New Chat | `Ctrl+N` | | Quit | `Ctrl+C` | ## MCP Integration Crush supports Model Context Protocol servers: - **Vision Server**: Analyze images and screenshots - **Web Search Server**: Search the web for information - **Web Reader Server**: Extract content from web pages ## Troubleshooting ### Configuration Not Working - Verify the config file location is correct - Check JSON syntax is valid - Ensure API key has no extra spaces ### Connection Issues - Verify your API key at [Apertis Dashboard](https://apertis.ai/setting?tab=keys) - Ensure the Base URL is `https://api.apertis.ai/v1` - Check your network connection ## Related Resources - [Model List](https://apertis.ai/models) - [API Keys Management](https://apertis.ai/setting?tab=keys) - [Claude Code](/installation/claude-code) - Alternative terminal AI coding tool - [OpenCode](/installation/opencode) - Another CLI AI coding agent --- Source: https://docs.apertis.ai/installation/goose # Goose Goose is an AI agent platform supporting local and desktop environments with CLI capabilities. It automates engineering tasks like code generation, debugging, testing, and deployment through MCP protocol integration. ## Features - **Code Generation**: Generate code from natural language descriptions - **Debugging**: Identify and fix issues in your code - **Testing**: Create and run tests - **Deployment**: Automate deployment workflows - **MCP Protocol**: Enhanced functionality through MCP servers - **Desktop App**: Native desktop application with GUI ## Installation ### Desktop Application Download Goose Desktop from the [official website](https://block.github.io/goose/docs/quickstart/) and install for your operating system. ### CLI Installation Follow the installation instructions from the official Goose documentation for CLI setup. ## Configuration for Apertis API ### Step 1: Open Provider Settings 1. Launch Goose Desktop 2. Click the menu on the left sidebar 3. Select **"Create New Provider"** ### Step 2: Configure Anthropic-Compatible Provider Enter the following settings: | Setting | Value | |---------|-------| | Provider Type | Anthropic Compatible | | Base URL | `https://api.apertis.ai` | | API Key | Your Apertis API key | ### Step 3: Select Model Choose your preferred model: | Model | Name | |-------|------| | Claude Sonnet 4.6 | `claude-sonnet-4-6` | | Claude Haiku 4.5 | `claude-haiku-4-5` | | Claude Sonnet 4 | `claude-sonnet-4-6` | | Claude Opus 4.8 | `claude-opus-4-8` | ### Step 4: Activate Provider 1. Return to the main interface 2. Click **"Switch Models"** at the bottom 3. Select your newly configured Apertis provider from the dropdown ## Alternative: OpenAI-Compatible Configuration You can also configure Goose with OpenAI-compatible settings: | Setting | Value | |---------|-------| | Provider Type | OpenAI Compatible | | Base URL | `https://api.apertis.ai/v1` | | API Key | Your Apertis API key | This allows access to all models including GPT and Gemini variants. ## Available Models | Model | Name | |-------|------| | Claude Sonnet 4.6 | `claude-sonnet-4-6` | | Claude Haiku 4.5 | `claude-haiku-4-5` | | GPT-5.5 | `gpt-5.5` | | GPT-5.4 mini | `gpt-5.4-mini` | | Gemini 3.5 Flash | `gemini-3.5-flash` | ## MCP Integration Goose supports Model Context Protocol servers for enhanced functionality: - **Vision Server**: Analyze images and visual content - **Web Search Server**: Search the web for information - **Web Reader Server**: Extract content from web pages ## Capabilities Once configured, Goose can assist with: - **Code Generation**: Create code based on requirements - **Debugging**: Identify and fix bugs - **Testing**: Generate and run test suites - **Deployment**: Automate CI/CD workflows - **Documentation**: Generate project documentation - **Refactoring**: Improve code structure ## Troubleshooting ### Provider Not Working - Verify your API key at [Apertis Dashboard](https://apertis.ai/setting?tab=keys) - Check the Base URL is correct for your provider type - Try switching between Anthropic and OpenAI compatible modes ### Connection Issues - Check your network connection - Verify firewall settings allow connections - Ensure the API key is valid and has sufficient quota ## Related Resources - [Model List](https://apertis.ai/models) - [API Keys Management](https://apertis.ai/setting?tab=keys) - [Claude Code](/installation/claude-code) - Terminal-based AI coding tool - [Goose Official Documentation](https://block.github.io/goose/docs/quickstart/) --- Source: https://docs.apertis.ai/installation/cline # Cline (prev. Claude Dev) ### Installing Cline - Install the `Cline` extension from the VSCode marketplace: [**Cline (prev. Claude Dev)**](https://marketplace.visualstudio.com/items?itemName=saoudrizwan.claude-dev) ### Using Apertis 1. Open the Cline extension settings by clicking the gear icon in the top-right corner and selecting `Settings`. 2. Set the **API Provider** field to `OpenAI Compatible`. 3. Enter `https://api.apertis.ai/v1` in the **Base URL** field. 4. Input your **API Key** in the **API Key** field. --- *Example configuration (using `Anthropic Claude 3.5 Sonnet 20240620`):* ![image](https://raw.githubusercontent.com/Apertis-AI/documentation/refs/heads/main/static/img/cline.png) **Once configured, you are ready to start using the API!** --- Source: https://docs.apertis.ai/installation/cursor # Cursor IDE ## Using Apertis Models ### Setting Up API Key You can integrate **Apertis** into **Cursor** to use its models. 1. Open Cursor Settings and navigate to the **Models** section from the sidebar. ![Cursor Settings Sidebar](/assets/images/cursor_settings_sidebar-1b7a8d9381395aae.png) 2. Scroll down to the **API Keys** section. In the **OpenAI API Key** field, enter your Apertis API Key. Enable the **Override OpenAI Base URL** toggle and enter the following URL: ``` https://api.apertis.ai/v1 ``` ![API Keys Configuration](/assets/images/cursor_api_keys-68ed3570c2260dd0.png) ### Enabling Models After configuring the API Key, enable the models you want to use by toggling them on in the Models list. The available models through Apertis include: - **Sonnet 4.6** — Claude Sonnet 4.6 - **Opus 4.8** — Claude Opus 4.8 - **GPT-5.3 Codex** — OpenAI GPT-5.3 Codex - **GPT-5.4** — OpenAI GPT-5.4 ![Models List](/assets/images/cursor_models_list-4b7c63e1113f4651.png) For the full list of supported models, refer to the [Model Support List](https://apertis.ai/models). > **Tip: Subscription Plans** > > If you are using an Apertis **Subscription Plan**, the models included in your plan are pre-configured. You can view and manage your subscription-supported models at [Apertis Settings → Models](https://apertis.ai/setting?tab=models). Once enabled, the models will be available in the chat and composer interfaces. ### Model ID Conflicts Some model IDs (e.g., `gpt-5.4`) may conflict with Cursor's built-in model routing, causing requests to bypass Apertis entirely. **Solution:** Add the `apertis/` prefix to the model ID. | Original | Cursor IDE Version | |----------|-------------------| | `gpt-5.4` | `apertis/gpt-5.4` | | `claude-sonnet-4-6` | `apertis/claude-sonnet-4-6` | | `code:claude-sonnet-4-6` | `apertis/code:claude-sonnet-4-6` | | `gpt-5.4:web` | `apertis/gpt-5.4:web` | The `apertis/` prefix is automatically stripped at the API layer — billing, logs, and quota are unaffected. You can find the Cursor-compatible model ID for each model on its detail page at [apertis.ai/models](https://apertis.ai/models). > **Note** > > The `apertis/` prefix is only needed when you encounter model conflicts in Cursor IDE. Other tools (Claude Code, Kilo Code, etc.) can use the original model ID directly. --- Source: https://docs.apertis.ai/installation/continue # Continue Dev [**Continue Dev**](https://www.continue.dev/) is an extension designed for **VSCode** and **Cursor IDE** that simplifies interactions with large language models such as ChatGPT. It allows users to effortlessly integrate code snippets into these models for development assistance. ### Introduction Compared to **Cursor IDE**, both tools showcase **code-centric interaction capabilities with language models** and provide **automatic code rewriting** features. However, Cursor IDE excels in Codebase RAG (Retrieval-Augmented Generation) and offers significantly higher precision in autocompletion. On the other hand, Continue Dev shines in quick queries and debugging tasks. As a lightweight extension, it provides an alternative option for immediate support. Whether seeking instant answers, optimization suggestions, or backup when Cursor IDE encounters issues, Continue Dev proves to be a reliable tool. ### Installation This guide focuses on the installation process for VSCode. Begin by navigating to the **Extensions** menu on the left-hand panel and searching for **Continue**. ![](/assets/images/continue_1-49e2db1d9ebd9cf2.jpg) After installation, the following interface will appear. However, the chat panel on the left will not function until the `config.json` file is configured. ![](/assets/images/continue_2-8f3962e8a10bffb2.jpg) ### Adding `config.json` Click the gear icon in the bottom-left corner to access the `config.json` file. ![](/assets/images/continue_3-dd1a7820cc937842.png) Copy the JSON configuration below and paste it into the `config.json` file. Replace the **API Key** with your **API Key**. ```json { "models": [ { "model": "claude-sonnet-4-6", "apiBase": "https://api.apertis.ai/v1", "title": "Claude Sonnet 4.6", "apiKey": "sk-xxxxxxxxxxxxxxxxxxxxx", "provider": "openai", "description": "Explain in details" }, { "model": "claude-haiku-4-5", "apiBase": "https://api.apertis.ai/v1", "title": "Claude Haiku 4.5", "apiKey": "sk-xxxxxxxxxxxxxxxxxxxxx", "provider": "openai", "description": "Explain in details" }, { "model": "claude-opus-4-8", "apiBase": "https://api.apertis.ai/v1", "title": "Claude Opus 4.8", "apiKey": "sk-xxxxxxxxxxxxxxxxxxxxx", "provider": "openai", "description": "Explain in details" }, { "model": "gpt-5.5", "apiBase": "https://api.apertis.ai/v1", "title": "GPT-5.5", "apiKey": "sk-xxxxxxxxxxxxxxxxxxxxx", "provider": "openai", "description": "Explain in details" }, { "model": "gpt-5.4-mini", "apiBase": "https://api.apertis.ai/v1", "title": "GPT-5.4 Mini", "apiKey": "sk-xxxxxxxxxxxxxxxxxxxxx", "provider": "openai", "description": "Explain in details" }, { "model": "gpt-5.4-nano", "apiBase": "https://api.apertis.ai/v1", "title": "GPT-5.4 Nano", "apiKey": "sk-xxxxxxxxxxxxxxxxxxxxx", "provider": "openai", "description": "Explain in details" }, { "model": "gemini-3.1-pro-preview", "apiBase": "https://api.apertis.ai/v1", "title": "gemini-3.1-pro-preview", "apiKey": "sk-xxxxxxxxxxxxxxxxxxxxx", "provider": "openai", "description": "Explain in details" }, { "model": "gemini-3.5-flash", "apiBase": "https://api.apertis.ai/v1", "title": "gemini-3.5-flash", "apiKey": "sk-xxxxxxxxxxxxxxxxxxxxx", "provider": "openai", "description": "Explain in details" } ], "customCommands": [ { "name": "test", "prompt": "{{{ input }}}\n\nWrite a comprehensive set of unit tests for the selected code. It should setup, run tests that check for correctness including important edge cases, and teardown. Ensure that the tests are complete and sophisticated. Give the tests just as chat output, don't edit any file.", "description": "Write unit tests for highlighted code" } ], "allowAnonymousTelemetry": true, "embeddingsProvider": { "provider": "free-trial" }, "tabAutocompleteModel": { "model": "gpt-5.5", "apiBase": "https://api.apertis.ai/v1", "title": "GPT-5.5", "apiKey": "sk-xxxxxxxxxxxxxxxxxxxxx", "provider": "openai" }, "tabAutocompleteOptions": { "useCopyBuffer": false, "maxPromptTokens": 400, "prefixPercentage": 0.5 }, "reranker": { "name": "free-trial" } } ``` ### Start Using **Ask Code (Chat Mode)** - 選取一段程式碼後【Ctrl + L】 / [Cmd + L] ![](/assets/images/continue_4-1675cf8876618402.png) **Rewrite Code (Edit Mode)** - 選取一段程式碼後【Ctrl + I】 / [Cmd + I] ![](/assets/images/continue_5-e0b71df0fbc86e71.png) --- Source: https://docs.apertis.ai/installation/roocode # Roo Code (prev. Roo Cline) Roo Code is a solution that enables developers to easily integrate Apertis into their projects. By providing comprehensive API endpoints and compatibility support, developers can quickly incorporate large language model capabilities into various applications. ![image](/assets/images/roocode_1-1c57778be21c7fd8.png) ## Features - **OpenAI Compatible API**: Fully compatible with OpenAI's API format, allowing you to easily convert existing OpenAI projects to use Apertis - **Multi-language Support**: Supports development in multiple programming languages including Python, Node.js, Java, and more - **Flexible Model Selection**: Choose from various models based on your needs, including GPT, Claude, Gemini series - **Simple Integration Process**: Integration requires only modifying the API endpoint and key ## Basic Configuration ### API Endpoint - **[https://api.apertis.ai/v1](https://api.apertis.ai/v1)** ### API Key Setup 1. Register and obtain your API key at [Apertis](https://apertis.ai/setting?tab=keys) 2. API keys start with `sk-` 3. Please keep your API key secure and do not share it publicly ## Installation Example - Select `Settings` and `API Providers` in the Roo Code settings page, and choose `OpenAI Compatible` API mode - Enter `https://api.apertis.ai/v1` in the `Base URL` field - Enter your API key in the `OpenAI API Key` field - Enter the model alias in the `Model` field, refer to [Model List](https://apertis.ai/models) ![image](/assets/images/roocode_2-464169a96431914d.png) After completing the input fields, you can start using the extension with the model you want! --- Source: https://docs.apertis.ai/installation/kilo-code # Kilo Code Kilo Code is a VS Code extension enhanced by MCP (Model Context Protocol) support, enabling direct code generation, debugging, and project management within the editor. ## Features - **Code Generation**: Generate code from natural language descriptions - **Database Analysis**: Analyze database structures and relationships - **SQL Optimization**: Generate and optimize SQL queries - **Project Management**: Manage and organize project files - **Refactoring**: Improve code quality and structure - **Documentation**: Create documentation for your codebase ## Installation ### Step 1: Install Extension 1. Launch VS Code 2. Open the Extensions Marketplace (left sidebar) 3. Search for "Kilo Code" 4. Click **Install** 5. Trust the developer when prompted ### Step 2: Configure API Key After installation, select **"Use your own API key"** option to configure custom API settings. ## Configure Apertis API ### API Provider Setup 1. Open Kilo Code settings 2. Select **OpenAI Compatible** as the API Provider 3. Enter the Base URL: `https://api.apertis.ai/v1` 4. Enter your API Key (obtain from [Apertis Dashboard](https://apertis.ai/setting?tab=keys)) 5. Select a model from the [Model List](https://apertis.ai/models) ### Configuration Summary | Setting | Value | |---------|-------| | API Provider | OpenAI Compatible | | Base URL | `https://api.apertis.ai/v1` | | API Key | Your Apertis API key | | Model | See model list below | ### Available Models | Model | Name to Enter | |-------|---------------| | Claude Sonnet 4.6 | `claude-sonnet-4-6` | | Claude Haiku 4.5 | `claude-haiku-4-5` | | GPT-5.5 | `gpt-5.5` | | GPT-5.4 mini | `gpt-5.4-mini` | | Gemini 3.5 Flash | `gemini-3.5-flash` | ## Capabilities Once configured, Kilo Code can assist with: - **Database Structure Analysis**: Understand and document database schemas - **Statistical Calculations**: Perform data analysis and calculations - **SQL Query Generation**: Create optimized SQL queries - **Code Generation**: Generate code based on requirements - **Project Analysis**: Analyze and document project structure - **Refactoring**: Suggest and implement code improvements ## MCP Integration Kilo Code supports MCP (Model Context Protocol) for enhanced functionality: - **Vision Server**: Analyze images and visual content - **Web Search Server**: Search the web for information - **Web Reader Server**: Extract content from web pages ## Troubleshooting ### Connection Issues - Verify your API key at [Apertis Dashboard](https://apertis.ai/setting?tab=keys) - Ensure the Base URL is `https://api.apertis.ai/v1` - Check that the model name matches the [Model List](https://apertis.ai/models) exactly ### Extension Not Working - Restart VS Code after configuration changes - Check the Output panel for error messages - Ensure you selected "Use your own API key" option ## Related Resources - [Model List](https://apertis.ai/models) - [API Keys Management](https://apertis.ai/setting?tab=keys) - [Roo Code Extension](/installation/roocode) - Alternative VS Code AI assistant - [Cline Extension](/installation/cline) - Another VS Code AI coding tool --- Source: https://docs.apertis.ai/installation/kilo-cli # Kilo CLI Kilo CLI is the terminal-based AI coding agent from Kilo Code, providing agentic coding capabilities directly in your terminal with multi-model support, MCP integration, and customizable agent workflows. ## Prerequisites - An Apertis API Key (obtain from [Apertis Dashboard](https://apertis.ai/setting?tab=keys)) ## Installation ```bash curl -fsSL https://kilo.ai/install | bash ``` ## Configuration Create or edit the configuration file at `~/.config/kilo/opencode.json`: ```json { "$schema": "https://kilo.ai/config.json", "model": "apertis/claude-sonnet-4-6", "small_model": "apertis/claude-haiku-4-5", "theme": "catppuccin", "autoupdate": true, "share": "manual", "snapshot": true, "logLevel": "INFO", "provider": { "apertis": { "options": { "apiKey": "your_apertis_api_key", "baseURL": "https://api.apertis.ai/v1" }, "models": { "claude-opus-4-8-thinking": { "id": "claude-opus-4-8-thinking", "name": "Claude Opus 4.8 Thinking (via Apertis)" }, "claude-opus-4-8": { "id": "claude-opus-4-8", "name": "Claude Opus 4.8 (via Apertis)" }, "claude-sonnet-4-6": { "id": "claude-sonnet-4-6", "name": "Claude Sonnet 4.6 (via Apertis)" }, "claude-haiku-4-5": { "id": "claude-haiku-4-5", "name": "Claude Haiku 4.5 (via Apertis)" }, "gemini-3.1-pro-preview": { "id": "gemini-3.1-pro-preview", "name": "Gemini 3.1 Pro Preview (via Apertis)" } } } }, "agent": { "build": { "model": "apertis/claude-sonnet-4-6", "temperature": 0.7, "maxSteps": 50, "permission": { "edit": "allow", "bash": { "npm": "allow", "git": "allow", "go": "allow", "make": "allow", "*": "ask" }, "webfetch": "allow" } }, "plan": { "model": "apertis/claude-opus-4-8-thinking", "permission": { "edit": "deny", "bash": "ask" } } }, "mcp": { "context7": { "type": "local", "command": ["npx", "-y", "@upstash/context7-mcp@latest"], "enabled": true } }, "instructions": ["./AGENTS.md", "./CLAUDE.md"], "keybinds": { "leader": "ctrl+k", "session_new": "ctrl+n", "agent_cycle": "tab" }, "lsp": { "typescript": { "command": ["typescript-language-server", "--stdio"], "extensions": [".ts", ".tsx", ".js", ".jsx"] } } } ``` Replace `your_apertis_api_key` with your actual API key from [Apertis Dashboard](https://apertis.ai/setting?tab=keys). ### Configuration Fields | Field | Description | |-------|-------------| | `model` | Default model for general use. Format: `apertis/`. | | `small_model` | Lightweight model for quick tasks. Format: `apertis/`. | | `provider.apertis.options.apiKey` | Your API key from [Apertis Dashboard](https://apertis.ai/setting?tab=keys). | | `provider.apertis.options.baseURL` | Apertis API endpoint. Typically no change needed. | | `provider.apertis.models` | Available models to register. Add or remove as needed. | | `agent.build` | Agent config for implementation tasks (model, permissions, max steps). | | `agent.plan` | Agent config for planning tasks (typically a thinking model). | | `mcp` | MCP server integrations (e.g., Context7, Supabase, Cloudflare). | | `instructions` | Instruction files to load (e.g., `AGENTS.md`, `CLAUDE.md`). | | `keybinds` | Custom keyboard shortcuts. | | `lsp` | Language server configurations for code intelligence. | ### Available Model IDs | Model ID | Description | |----------|-------------| | `claude-opus-4-8-thinking` | Claude Opus 4.8 with extended thinking | | `claude-opus-4-8` | Claude Opus 4.8 — most capable | | `claude-sonnet-4-6` | Claude Sonnet 4.6 — balanced speed and capability | | `claude-haiku-4-5` | Claude Haiku 4.5 — fastest | | `gemini-3.1-pro-preview` | Gemini 3.1 Pro Preview | For the full list of available models, check the [Model List](https://apertis.ai/models). ### Agent Modes Kilo CLI supports multiple agent modes with different model and permission configurations: | Agent | Purpose | Recommended Model | |-------|---------|-------------------| | `build` | Implementation, coding, file editing | `apertis/claude-sonnet-4-6` | | `plan` | Architecture planning, read-only analysis | `apertis/claude-opus-4-8-thinking` | ### MCP Server Examples Add MCP servers to extend Kilo CLI's capabilities: ```json { "mcp": { "supabase": { "type": "remote", "url": "https://mcp.supabase.com/mcp?project_ref=your_project_ref" }, "cloudflare-observability": { "type": "local", "command": ["npx", "mcp-remote", "https://observability.mcp.cloudflare.com/mcp"], "enabled": true }, "context7": { "type": "local", "command": ["npx", "-y", "@upstash/context7-mcp@latest"], "enabled": true } } } ``` ## Launch ```bash kilo ``` ## Keybinds | Keybind | Action | |---------|--------| | `ctrl+k` | Leader key | | `ctrl+n` | New session | | `tab` | Cycle between agents | ## Troubleshooting ### Connection Issues - Verify your API key at [Apertis Dashboard](https://apertis.ai/setting?tab=keys) - Ensure `baseURL` in config is `https://api.apertis.ai/v1` - Check your network connection and firewall settings ### Config Not Loading - Verify the config file is at `~/.config/kilo/opencode.json` - Validate JSON syntax (no trailing commas, correct quotes) - Restart Kilo CLI after config changes ## Related Resources - [Model List](https://apertis.ai/models) - [API Keys Management](https://apertis.ai/setting?tab=keys) - [Kilo Code Extension](/installation/kilo-code) - VS Code extension version - [OpenCode](/installation/opencode) - Alternative terminal AI coding tool - [Claude Code](/installation/claude-code) - Anthropic's terminal AI coding tool --- Source: https://docs.apertis.ai/installation/chatbox # Chatbox AI Chatbox AI is a graphical chat interface designed for **interacting with APIs via API Keys in a user-friendly UI**. Unlike using OpenAI or Claude's official interfaces, which often require **subscription fees or impose usage limits**, or purchasing an API Key that demands programming knowledge to create a custom interface, Chatbox AI eliminates these barriers. It allows users to seamlessly interact with **Apertis and over 60 models** through a familiar interface, enabling **one-click switching between different models**, making it easy to select the best model for specific tasks. ### Download and Installation Visit the **[Chatbox AI Official Website](https://chatboxai.app/zh-TW)** to download the appropriate version for your computer system. Chatbox AI also offers mobile versions for Android and iOS. ![image](/assets/images/chatbox_0-f6502e91c9e9c01f.png) ![image](/assets/images/chatbox_1-64f44e60e420f581.png) ### Setting Up API Key Click the settings icon in the bottom-left corner to access the **Model Configuration** screen. Under **Model Providers**, select `Add Custom Provider`. Then, choose `OpenAI API Compatible` in the `API Mode` dropdown. Since we provide models in OpenAI API-compatible formats, you can directly use this option. Enter your API Key in the **API Key** field and set **API Base URL** to `https://api.apertis.ai`. Select the model you wish to use. Adjust additional parameters like `temperature` and `Top P` if needed, then click Save to start using the model. ![image](/assets/images/chatbox_2-a97c69206769531e.png) For example, to use **Claude Sonnet 4.6**, input the following settings: - **API Mode**: OpenAI API Compatible - **Name**: Any name (e.g., "Claude Sonnet 4.6" for easy identification) - **API Base URL**: `https://api.apertis.ai` - **API Path**: No changes needed - **API Key**: Your API Key - **Model**: `claude-sonnet-4-6` ![image](/assets/images/chatbox_3-d3f8761f5571f465.png) Click Save, then select the model you just configured to begin using it. ![image](/assets/images/chatbox_4-6f0890c27f200e5f.png) --- Source: https://docs.apertis.ai/installation/translate # Immersive Translation This is a **bilingual translation extension**. Unlike traditional **Google Translate** which directly overlays translations on the original text, requiring you to revert the translation to view the original text, Immersive Translation **inserts translations below the original text** for bilingual parallel reading. When translations are confusing, you can directly reference the original text. Additionally, Immersive Translation **supports current popular large language models** such as `GPT-5.5`, `Gemini 3.5 Flash`, etc., significantly improving translation accuracy. Besides webpage translation, Immersive Translation officially provides features like: **full PDF translation**, **real-time YouTube subtitle translation**, and **EPUB e-book translation**. Below, we'll detail how to install and use the models provided by **Apertis**. ### Getting the Immersive Translation Extension First, search for "Immersive Translation" in the Google or Edge extension store. The Google [installation link](https://chromewebstore.google.com/detail/%E6%B2%89%E6%B5%B8%E5%BC%8F%E7%BF%BB%E8%AD%AF-%E7%B6%B2%E9%A0%81%E7%BF%BB%E8%AD%AF%E6%93%B4%E5%85%85-pdf%E7%BF%BB%E8%AD%AF-%E5%85%8D%E8%B2%BB/bpoadfkcbjbfhfodiogcnhhhpibjhbnh) is here, follow the standard extension installation steps. ![](https://hackmd.io/_uploads/Sy__11WL1g.png) ![](https://hackmd.io/_uploads/ry8_lVdoA.png) ### Setting Up the Service Provider - Apertis After installation, you should find Immersive Translation in the top right corner. Click it and select 【Options】 in the bottom left. ![](https://hackmd.io/_uploads/HJ4O-VuoC.png) Please log in to your account in the basic settings, choosing any login method. Although you won't use the subscription features, login is required to use the extension. ![](https://hackmd.io/_uploads/B1YpWE_oA.png) In the service provider selection below, choose 【Custom API Key】 and click 【Show More Options】 - **APIKEY**: Enter your Apertis Key - **Model**: Choose one from gpt-5.4-mini (recommended), gpt-5.4-nano - **Custom API URL**: `https://api.apertis.ai` ![](https://hackmd.io/_uploads/SkNYXNdo0.png) ![](https://hackmd.io/_uploads/H1de44uoC.png) After entering, click 【Test Service】 in the top right. A successful validation message indicates it's ready to use. ![](https://hackmd.io/_uploads/SkL44VdiC.png) ### Setting Up Models - Claude, Gemini In the same interface, but with changes to the model section. - **APIKEY**: Enter your Apertis Key - **Model**: Please select 【Set More Models】 - **Custom API URL**: `https://api.apertis.ai` ![](https://hackmd.io/_uploads/B1PfrNOsR.png) Enter `gemini-3.5-flash, glm-4.7-flash, claude-haiku-4-5` in the box and save. ![](https://hackmd.io/_uploads/rJmIr4_iA.png) Subsequently, three additional models will appear in the original 【Model】 selection area. ![](https://hackmd.io/_uploads/Hyv_IN_iR.png) ### Using Webpage Translation Go to the webpage you want to translate, press 【Alt + A】 or click the **floating icon on the right side of the screen** to start using it. ![](https://hackmd.io/_uploads/HJQxwNdo0.png) ### Real-time Subtitle Translation Here you can see the supported video websites. ![](https://hackmd.io/_uploads/ByT7uNdiC.jpg) Find a video on Youtube that has subtitles (any language works). You'll find the Immersive Translation 【Bilingual Subtitles】 icon in the bottom right. Click to select translate subtitles to use. ![](https://hackmd.io/_uploads/BJy9MA2qR.png) ![](https://hackmd.io/_uploads/BJlgXQRhqA.png) ### Translating PDF Documents Click the icon in the top right corner, select 【PDF/epub】 to enter the PDF translation interface. If the current page is a PDF, the system will automatically redirect to this interface for translation. ![](https://hackmd.io/_uploads/HJGmDN_jC.png) Upload the PDF document you want to translate. ![](https://hackmd.io/_uploads/H1AZER39C.png) --- Source: https://docs.apertis.ai/installation/bolt_diy # bolt.diy (prev. oTToDev) bolt.diy is a solution that enables developers to easily integrate Apertis into their projects. By providing comprehensive API endpoints and compatibility support, developers can quickly incorporate large language model capabilities into various applications. ## Features - **OpenAI Compatible API**: Fully compatible with OpenAI's API format, allowing you to easily convert existing OpenAI projects to use Apertis - **Multi-language Support**: Supports development in multiple programming languages including Python, Node.js, Java, and more - **Flexible Model Selection**: Choose from various models based on your needs, including GPT, Claude, Gemini series - **Simple Integration Process**: Integration requires only modifying the API endpoint and key ## Basic Configuration ### API Endpoint - **[https://api.apertis.ai](https://api.apertis.ai)** ### API Key Setup 1. Register and obtain your API key at [Apertis](https://apertis.ai/setting?tab=keys) 2. API keys start with `sk-` 3. Please keep your API key secure and do not share it publicly ## Installation Example - Select `Provider` in the bolt.diy settings page, and choose `OpenAILike` API mode - Enter `https://api.apertis.ai` in the `Base URL` field ![image](/assets/images/bolt_1-90f736a20d9fa18a.png) - Enter your API key in the `API Key` field ![image](/assets/images/bolt_2-f7e02c6d317396b8.png) - After completing the input, you will see the following screen ![image](/assets/images/bolt_3-6923f2689ea6b271.png) Wait for a moment, and you will see all Apertis models in the model list! Select your desired model to start using it --- Source: https://docs.apertis.ai/installation/connection # Base URL and API Key Connection Guide Apertis AI provides `BASE_URL` and `API Key` configurations for integrating with custom projects. Different applications may require different `BASE_URL` options. ### BASE_URL The following three connection endpoints are available: - **[https://api.apertis.ai](https://api.apertis.ai)** - **[https://api.apertis.ai/v1](https://api.apertis.ai/v1)** - **[https://api.apertis.ai/v1/chat/completions](https://api.apertis.ai/v1/chat/completions)** ### API Key Visit **[Apertis](https://apertis.ai/setting?tab=keys)** to obtain your `API Key`. --- Source: https://docs.apertis.ai/installation/scripts # Call Apertis via OpenAI SDK ### Python Apertis provides **OpenAI Compatible APIs**, so you can use the `OpenAI` package in `Python` and modify the `model` variable to call all supported models. ```python key = '' # Please replace with your API Key from openai import OpenAI client = OpenAI( api_key=key, base_url="https://api.apertis.ai/v1/" ) chat_completion = client.chat.completions.create( model="", # Modify the model variable to call different models messages=[ {"role": "user", "content": "Hello world"} ] ) print(chat_completion.choices[0].message.content) ``` ### JavaScript ```javascript const apiKey = ''; const apiUrl = 'https://api.apertis.ai/v1/chat/completions'; async function callOpenAI(prompt) { const response = await fetch(apiUrl, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${apiKey}` }, body: JSON.stringify({ model: '', messages: [{ role: 'user', content: prompt }], max_tokens: 150, temperature: 0.7 }) }); const data = await response.json(); return data.choices[0].message.content; } callOpenAI('Hello world').then(reply => console.log(reply)); ``` ### TypeScript ```typescript import OpenAI from 'openai'; const openai = new OpenAI({ apiKey: '', // Please ensure that the Apertis key is set in the environment variables baseURL: 'https://api.apertis.ai/v1', // Replace with Apertis endpoint }); async function getChatCompletion(prompt: string): Promise { try { const chatCompletion = await openai.chat.completions.create({ model: '', // Replace with the model you want to use messages: [{ role: 'user', content: prompt }], max_tokens: 150, temperature: 0.7, }); console.log(chatCompletion.choices[0].message.content); } catch (error) { console.error('Error fetching chat completion:', error); } } // Example usage getChatCompletion('Hello world'); ``` ### cURL To use `cURL` to call Apertis, you only need to modify the `api_key` and `model` variables to call all supported models. Please replace `` and `model` values with your **API Key** and **model alias**. ```bash curl https://api.apertis.ai/v1/chat/completions \ -H "Content-Type: application/json" \ -H "Authorization: Bearer " \ -d '{ "model": "", "messages": [ { "role": "system", "content": "Hello World!" } ] }' ``` --- Source: https://docs.apertis.ai/security/best-practices # Security Best Practices Protecting your API keys and data is critical. This guide covers security best practices for using the Apertis API safely. ## API Key Security ### Never Expose Keys in Client-Side Code API keys should never be visible in browser or mobile app code: ```javascript // ❌ WRONG - Key exposed in frontend const apiKey = "sk-abc123..."; fetch('https://api.apertis.ai/v1/chat/completions', { headers: { 'Authorization': `Bearer ${apiKey}` } }); // ✅ CORRECT - Use backend proxy fetch('/api/chat', { method: 'POST', body: JSON.stringify({ message: userMessage }) }); ``` ### Use Environment Variables Store API keys in environment variables, not in code: ```bash # .env file (add to .gitignore!) APERTIS_API_KEY=sk-your-api-key ``` ```python import os from openai import OpenAI client = OpenAI( api_key=os.environ.get("APERTIS_API_KEY"), base_url="https://api.apertis.ai/v1" ) ``` ### Never Commit Keys to Git Add sensitive files to `.gitignore`: ```gitignore # .gitignore .env .env.local .env.production *.key config/secrets.yml ``` If you accidentally commit a key: 1. **Immediately revoke** the exposed key in the dashboard 2. **Create a new key** with fresh credentials 3. **Remove from git history** using `git filter-branch` or BFG Repo-Cleaner 4. **Rotate any related credentials** ### Use Secrets Management For production environments, use proper secrets management: | Platform | Solution | |----------|----------| | AWS | AWS Secrets Manager, Parameter Store | | GCP | Secret Manager | | Azure | Key Vault | | Kubernetes | Secrets, External Secrets Operator | | Docker | Docker Secrets | | CI/CD | GitHub Secrets, GitLab CI Variables | **Example with AWS Secrets Manager:** ```python import boto3 import json def get_api_key(): client = boto3.client('secretsmanager') response = client.get_secret_value(SecretId='apertis/api-key') secret = json.loads(response['SecretString']) return secret['api_key'] ``` ## Access Control ### Principle of Least Privilege Grant minimum necessary permissions: | Use Case | Recommended Settings | |----------|---------------------| | Development | Low quota, all models | | Testing | Limited quota, test models only | | Production | Appropriate quota, specific models | | Third-party | Minimal quota, restricted models | ### Model Restrictions Limit which models each key can access: ``` Production Key: ✓ gpt-5.5 ✓ gpt-5.4-mini ✗ claude-opus-4-8 (expensive) ✗ experimental models ``` ### IP Whitelisting Restrict API key usage to known IP addresses: ``` Allowed IPs: - 203.0.113.0/24 (Office network) - 198.51.100.50 (Production server) - 192.0.2.100 (CI/CD runner) ``` **Setting up IP whitelist:** 1. Go to **API Keys** in dashboard 2. Edit the key 3. Add allowed IPs/subnets to **Subnet Whitelist** 4. Save changes ### Key Rotation Regularly rotate API keys: | Environment | Rotation Frequency | |-------------|-------------------| | Development | Monthly | | Production | Quarterly | | After incident | Immediately | **Rotation process:** 1. Create new key with same permissions 2. Update applications to use new key 3. Verify new key works in production 4. Disable old key 5. Delete old key after grace period ## Secure Architecture ### Backend Proxy Pattern Never call the API directly from client applications: ``` ┌──────────┐ ┌──────────────┐ ┌─────────────┐ │ Client │────▶│ Your Backend │────▶│ Apertis API │ │ (no key) │ │ (has key) │ │ │ └──────────┘ └──────────────┘ └─────────────┘ ``` **Example proxy implementation (Node.js/Express):** ```javascript import express from 'express'; import OpenAI from 'openai'; const app = express(); const client = new OpenAI({ apiKey: process.env.APERTIS_API_KEY, baseURL: 'https://api.apertis.ai/v1' }); app.post('/api/chat', async (req, res) => { // Validate and sanitize user input const { message } = req.body; if (!message || message.length > 10000) { return res.status(400).json({ error: 'Invalid message' }); } try { const response = await client.chat.completions.create({ model: 'gpt-5.5', messages: [{ role: 'user', content: message }] }); res.json({ reply: response.choices[0].message.content }); } catch (error) { console.error('API error:', error); res.status(500).json({ error: 'Internal server error' }); } }); ``` ### Rate Limiting Your Backend Protect your backend from abuse: ```javascript import rateLimit from 'express-rate-limit'; const limiter = rateLimit({ windowMs: 60 * 1000, // 1 minute max: 20, // 20 requests per minute per IP message: { error: 'Too many requests' } }); app.use('/api/', limiter); ``` ### Request Validation Always validate user input before sending to the API: ```python from pydantic import BaseModel, validator class ChatRequest(BaseModel): message: str model: str = "gpt-5.5" @validator('message') def validate_message(cls, v): if len(v) > 10000: raise ValueError('Message too long') if len(v) < 1: raise ValueError('Message required') return v.strip() @validator('model') def validate_model(cls, v): allowed_models = ['gpt-5.5', 'gpt-5.4-mini', 'claude-sonnet-4-6'] if v not in allowed_models: raise ValueError(f'Model must be one of {allowed_models}') return v ``` ## Data Protection ### Sensitive Data Handling Never send sensitive data to the API unnecessarily: ```python # ❌ WRONG - Sending real PII response = client.chat.completions.create( model="gpt-5.5", messages=[{ "role": "user", "content": f"Summarize this customer record: {customer_data}" # Contains: SSN, credit card, address, etc. }] ) # ✅ CORRECT - Anonymize or use placeholders anonymized_data = anonymize(customer_data) response = client.chat.completions.create( model="gpt-5.5", messages=[{ "role": "user", "content": f"Summarize this record: {anonymized_data}" }] ) ``` ### Data Anonymization Remove or mask PII before sending: ```python import re def anonymize_text(text): # Mask email addresses text = re.sub(r'[\w\.-]+@[\w\.-]+', '[EMAIL]', text) # Mask phone numbers text = re.sub(r'\b\d{3}[-.]?\d{3}[-.]?\d{4}\b', '[PHONE]', text) # Mask credit card numbers text = re.sub(r'\b\d{4}[-\s]?\d{4}[-\s]?\d{4}[-\s]?\d{4}\b', '[CARD]', text) # Mask SSN text = re.sub(r'\b\d{3}[-]?\d{2}[-]?\d{4}\b', '[SSN]', text) return text ``` ### Logging Best Practices Be careful what you log: ```python import logging logger = logging.getLogger(__name__) # ❌ WRONG - Logging sensitive data logger.info(f"Request: {full_request}") # May contain API key or PII # ✅ CORRECT - Log safely logger.info(f"Request to model {model}, tokens: {token_count}") ``` ## Authentication Security ### Session Management For applications with user accounts: ```python # Use secure session settings app.config.update( SESSION_COOKIE_SECURE=True, # HTTPS only SESSION_COOKIE_HTTPONLY=True, # No JavaScript access SESSION_COOKIE_SAMESITE='Lax', # CSRF protection PERMANENT_SESSION_LIFETIME=3600 # 1 hour expiry ) ``` ### Multi-Factor Authentication Enable 2FA on your Apertis account: 1. Go to **Account Settings** 2. Enable **Two-Factor Authentication** 3. Use an authenticator app (Google Authenticator, Authy) 4. Save backup codes securely ## Network Security ### HTTPS Only Always use HTTPS for API calls: ```python # ✅ CORRECT - HTTPS base_url = "https://api.apertis.ai/v1" # ❌ WRONG - HTTP (insecure) base_url = "http://api.apertis.ai/v1" ``` ### Certificate Validation Never disable SSL certificate validation: ```python # ❌ DANGEROUS - Never do this in production client = OpenAI( api_key="sk-xxx", base_url="https://api.apertis.ai/v1", http_client=httpx.Client(verify=False) # INSECURE! ) # ✅ CORRECT - Use proper certificates client = OpenAI( api_key="sk-xxx", base_url="https://api.apertis.ai/v1" ) ``` ### Firewall Configuration Restrict outbound connections: ``` # Allow only necessary outbound ALLOW outbound to api.apertis.ai:443 DENY outbound to all other ``` ## Monitoring & Alerting ### Usage Monitoring Monitor for unusual patterns: | Alert Condition | Possible Issue | |-----------------|----------------| | Sudden spike in usage | Key compromised or bug | | Requests from new IPs | Unauthorized access | | High error rates | Abuse or misconfiguration | | After-hours activity | Unauthorized use | ### Security Logging Log security-relevant events: ```python import logging security_logger = logging.getLogger('security') def log_api_call(user_id, model, status): security_logger.info({ 'event': 'api_call', 'user_id': user_id, 'model': model, 'status': status, 'timestamp': datetime.utcnow().isoformat(), 'ip': request.remote_addr }) ``` ### Incident Response If you suspect a security breach: 1. **Immediately revoke** affected API keys 2. **Review logs** for unauthorized access 3. **Create new keys** with updated permissions 4. **Update applications** with new keys 5. **Investigate** the root cause 6. **Document** the incident and response 7. **Implement** preventive measures ## Compliance Considerations ### Data Residency Be aware of where data is processed: - API requests may be processed in various regions - Consider compliance requirements (GDPR, HIPAA, etc.) - Contact support for specific data residency needs ### Audit Trails Maintain audit logs for compliance: ```python def audit_log(action, user, details): log_entry = { 'timestamp': datetime.utcnow().isoformat(), 'action': action, 'user': user, 'details': details, 'ip_address': get_client_ip() } # Store in audit database audit_db.insert(log_entry) ``` ### Data Retention Define clear data retention policies: | Data Type | Retention | Reason | |-----------|-----------|--------| | API logs | 90 days | Debugging | | Audit logs | 1 year | Compliance | | User data | As needed | Business | ## Security Checklist ### Before Going Live - [ ] API keys stored in environment variables - [ ] Keys not committed to version control - [ ] Backend proxy implemented (no client-side API calls) - [ ] IP whitelisting configured - [ ] Model restrictions set - [ ] Rate limiting implemented - [ ] Input validation in place - [ ] Error handling doesn't expose sensitive info - [ ] HTTPS enforced - [ ] Logging configured (without sensitive data) ### Regular Review - [ ] Rotate API keys quarterly - [ ] Review access logs monthly - [ ] Audit API key permissions - [ ] Update dependencies for security patches - [ ] Test incident response procedures - [ ] Review and update security policies ## Related Topics - [API Keys](/authentication/api-keys) - Key management - [Rate Limits](/billing/rate-limits) - Request limits - [Error Codes](/help/error-codes) - Error handling - [Troubleshooting](/help/troubleshooting) - Common issues --- Source: https://docs.apertis.ai/help/faq # Frequently Asked Questions Quick answers to common questions about the Apertis API. ## General Questions ### What is Apertis? Apertis is a unified API platform that provides access to models from OpenAI, Anthropic, Google, and other providers through a single, OpenAI-compatible interface. Instead of managing multiple provider keys and SDKs, you can use one Apertis API key and confirm the models currently available to that key through the live catalog. ### How is Apertis different from using providers directly? | Feature | Direct Provider | Apertis | |---------|----------------|---------| | API Keys | One per provider | Single unified key | | SDK | Different per provider | Standard OpenAI SDK | | Billing | Separate per provider | Unified billing | | Models | Provider's models only | Live multi-provider catalog | | Fallback | Manual implementation | Built-in | ### Is Apertis compatible with OpenAI SDK? Yes! Apertis is fully compatible with the OpenAI SDK. You only need to change the base URL: ```python from openai import OpenAI client = OpenAI( api_key="sk-your-apertis-key", base_url="https://api.apertis.ai/v1" # Only change needed ) ``` ### What models are available? Apertis provides access to a changing multi-provider catalog that includes: - **OpenAI**: GPT-5.5, GPT-5.4 Mini, GPT-5.4 Nano - **Anthropic**: Claude Opus 4.8, Claude Sonnet 4.6, Claude Fable 5 - **Google**: Gemini 3.1 Pro, Gemini 3.5 Flash - **Open Source**: Llama, Mistral, and more [View the current model catalog →](https://apertis.ai/models) --- ## Account & Authentication ### How do I get an API key? 1. [Create an Apertis account](https://apertis.ai/register) or log in. 2. Choose a [Coding Plan](https://apertis.ai/subscribe) or add [PAYG balance](https://apertis.ai/setting?tab=credits). 3. Open [**Settings → API Keys**](https://apertis.ai/setting?tab=keys). 4. Click **Create New Key** and securely store the key. ### I lost my API key. Can I recover it? No, API keys are only displayed once when created. If you lose your key: 1. Create a new API key 2. Update your applications 3. Delete the old key for security ### How many API keys can I create? There's no strict limit. You can create multiple keys for different: - Environments (dev, staging, production) - Applications - Team members ### Can I share my API key? We recommend creating separate keys for each user or application. This allows: - Individual quota tracking - Easy revocation if compromised - Better security audit trails --- ## Billing & Pricing ### How does billing work? Apertis offers two billing models: 1. **Subscription Plans**: Monthly quota with fixed pricing 2. **Pay-As-You-Go (PAYG)**: Pay per token used [Learn more about billing →](/billing/subscription-plans) ### What's the difference between subscription and PAYG? | Aspect | Subscription | PAYG | |--------|-------------|------| | Commitment | Monthly/Yearly | None | | Pricing | Discounted | Standard | | Best for | Predictable usage | Variable usage | ### How is usage calculated? Usage is calculated per token: ``` Cost = (Input Tokens × Input Rate) + (Output Tokens × Output Rate) ``` Different models have different rates. [View current pricing →](https://apertis.ai/models) ### What happens when I run out of quota? - **Subscription**: Enable PAYG fallback to continue, or wait for cycle reset - **PAYG**: Add funds to continue ### Do unused quotas roll over? No, subscription quotas reset at each billing cycle and don't roll over. ### Can I get a refund? Contact support at hi@apertis.ai for refund requests. Refunds are handled case-by-case. --- ## Technical Questions ### What's the API base URL? ``` https://api.apertis.ai/v1 ``` ### What's the rate limit? | Limit Type | Value | |------------|-------| | API requests | 1,500 per 3 minutes per IP | | Per API key | 3,000 per minute | [Learn more about rate limits →](/billing/rate-limits) ### Do you support streaming? Yes! Enable streaming with `stream=True`: ```python response = client.chat.completions.create( model="gpt-5.5", messages=[{"role": "user", "content": "Hello"}], stream=True ) for chunk in response: print(chunk.choices[0].delta.content, end="") ``` ### What's the maximum context length? It depends on the model: | Model | Context Length | |-------|----------------| | GPT-5.5 | 128K tokens | | Claude Sonnet 4.6 | 200K tokens | | Gemini 3.1 Pro | 1M tokens | ### Do you support function calling? Yes, function calling is supported for compatible models: ```python response = client.chat.completions.create( model="gpt-5.5", messages=[{"role": "user", "content": "What's the weather?"}], tools=[{ "type": "function", "function": { "name": "get_weather", "parameters": {...} } }] ) ``` ### Do you support vision (image input)? Yes, for models that support it: ```python response = client.chat.completions.create( model="gpt-5.5", messages=[{ "role": "user", "content": [ {"type": "text", "text": "What's in this image?"}, {"type": "image_url", "image_url": {"url": "https://..."}} ] }] ) ``` ### What audio formats are supported? For speech-to-text: MP3, MP4, MPEG, MPGA, M4A, WAV, WebM For text-to-speech: MP3, Opus, AAC, FLAC, WAV, PCM --- ## Integrations ### Does Apertis work with LangChain? Yes: ```python from langchain_openai import ChatOpenAI llm = ChatOpenAI( model="gpt-5.5", openai_api_key="sk-your-key", openai_api_base="https://api.apertis.ai/v1" ) ``` ### Does Apertis work with Cursor/Cline/Continue? Yes! Apertis works with all OpenAI-compatible tools: - [Cursor Integration](/installation/cursor) - [Cline Integration](/installation/cline) - [Continue Integration](/installation/continue) ### Can I use Apertis with my existing OpenAI code? Yes, just change the base URL and API key. No other code changes needed. --- ## Models & Features ### Which model should I use? | Use Case | Recommended Model | |----------|-------------------| | General chat | GPT-5.5 | | Fast & cheap | GPT-5.4 mini | | Complex reasoning | Claude Opus 4.8 | | Long documents | Claude Sonnet 4.6 | | Code generation | GPT-5.5 or Claude Sonnet 4.6 | ### Are model responses the same as direct providers? Yes, Apertis routes requests to the actual providers. Responses are identical to what you'd get directly. ### What is fallback models? Fallback automatically switches to an alternative model if your primary model fails: ```python response = client.chat.completions.create( model="gpt-5.5", messages=[...], extra_body={ "fallback_models": ["claude-sonnet-4-6", "gemini-3.1-pro-preview"] } ) ``` [Learn more about fallback →](/api/utilities/fallback-models) ### Is there a model playground? No. The Apertis Playground has been retired. To try a model, send your first request with the [Quick Start](/getting-started/quick-start), or compare models in the [model catalog](https://apertis.ai/models). --- ## Security & Privacy ### Is my data secure? Yes. We implement industry-standard security practices: - HTTPS/TLS encryption for all connections - API keys are encrypted at rest - No logging of request/response content by default ### Do you store my prompts or responses? By default, we don't store prompt or response content. Only metadata (token counts, timestamps) is logged for billing. ### Is Apertis SOC 2 compliant? Contact support for compliance documentation and enterprise security features. ### Can I restrict API key by IP? Yes, you can set IP whitelists for each API key in the dashboard. --- ## Troubleshooting ### I'm getting 401 Unauthorized - Check your API key is correct - Ensure the `Authorization: Bearer` format is used - Verify the key hasn't been deleted or expired ### I'm getting 429 Too Many Requests - You've hit the rate limit - Wait and retry with exponential backoff - Consider upgrading your plan for higher limits ### I'm getting 402 Payment Required - Your quota is exhausted - Add funds (PAYG) or wait for cycle reset (subscription) - Enable PAYG fallback for subscriptions ### My requests are slow - Use streaming for long responses - Check your network connection - Some models are slower than others [Full troubleshooting guide →](/help/troubleshooting) --- ## Account Management ### How do I change my email? 1. Go to **Account Settings** 2. Click **Change Email** 3. Verify the new email address ### How do I delete my account? Contact support at hi@apertis.ai to request account deletion. ### Can I have multiple accounts? We recommend using a single account with multiple API keys instead of multiple accounts. --- ## Support ### How do I contact support? - **Email**: hi@apertis.ai - **Dashboard**: Submit a ticket ### What are support hours? We aim to respond within 24 hours on business days. ### Is there a status page? Yes: [status.apertis.ai](https://status.apertis.ai) --- ## Still have questions? - Check our [Troubleshooting Guide](/help/troubleshooting) - Review [Error Codes](/help/error-codes) - Contact support at hi@apertis.ai --- Source: https://docs.apertis.ai/help/troubleshooting # Troubleshooting This guide helps you diagnose and resolve common issues when using the Apertis API. ## Quick Diagnostics ### Test Your API Key Verify your API key is working: ```bash curl https://api.apertis.ai/v1/models \ -H "Authorization: Bearer sk-your-api-key" ``` **Expected Response:** ```json { "object": "list", "data": [ {"id": "gpt-5.5", "object": "model", ...}, ... ] } ``` ### Check API Status Visit our status page: [status.apertis.ai](https://status.apertis.ai) ## Common Issues ### Authentication Errors #### 401 Unauthorized **Symptoms:** ```json { "error": { "message": "Invalid API key", "type": "authentication_error", "code": "invalid_api_key" } } ``` **Causes & Solutions:** | Cause | Solution | |-------|----------| | Missing API key | Add `Authorization: Bearer sk-xxx` header | | Typo in API key | Copy key directly from dashboard | | Expired API key | Check expiration date, create new key | | Deleted API key | Create a new API key | **Debug Steps:** 1. Verify the `Authorization` header format: ``` Authorization: Bearer sk-your-api-key ``` Note: Include `Bearer ` (with space) before the key 2. Check for invisible characters: ```python api_key = api_key.strip() # Remove whitespace ``` 3. Test with a fresh key to rule out key issues #### 403 Forbidden **Symptoms:** ```json { "error": { "message": "Access denied", "type": "permission_error", "code": "access_denied" } } ``` **Causes & Solutions:** | Cause | Solution | |-------|----------| | Model not in whitelist | Check key's model restrictions | | IP not whitelisted | Add your IP to allowed list | | Account suspended | Contact support | ### Quota & Billing Errors #### 402 Payment Required **Symptoms:** ```json { "error": { "message": "Quota exceeded", "type": "billing_error", "code": "quota_exceeded" } } ``` **Solutions:** 1. **Check your quota** in the dashboard 2. **Top up your balance** if using PAYG 3. **Enable PAYG fallback** for subscription users: - Dashboard → Billing → Enable PAYG Fallback 4. **Upgrade your plan** for more quota #### Subscription Token Not Working If your subscription token (`sk-sub-xxx`) isn't working: 1. **Check subscription status:** - Active? Suspended? Expired? 2. **Verify quota remaining:** - Subscription quota may be exhausted - PAYG fallback may be disabled 3. **Check billing cycle:** - Quota resets at cycle start - View cycle dates in dashboard ### Rate Limiting #### 429 Too Many Requests **Symptoms:** ```json { "error": { "message": "Rate limit exceeded", "type": "rate_limit_error", "code": "rate_limit_exceeded" } } ``` **Immediate Actions:** 1. **Wait and retry** - Rate limits reset after the window expires 2. **Check rate limit headers** for reset time: ``` X-RateLimit-Reset: 1703894400 ``` **Long-term Solutions:** 1. **Implement exponential backoff:** ```python import time def retry_with_backoff(func, max_retries=5): for i in range(max_retries): try: return func() except RateLimitError: wait = (2 ** i) + random.random() time.sleep(wait) raise Exception("Max retries exceeded") ``` 2. **Reduce request frequency** - Batch operations when possible 3. **Use multiple API keys** to distribute load 4. **Upgrade your plan** for higher limits ### Model Errors #### Model Not Found **Symptoms:** ```json { "error": { "message": "Model 'xyz' not found", "type": "invalid_request_error", "code": "model_not_found" } } ``` **Solutions:** 1. **Check model name spelling:** ```python # Correct model = "gpt-5.5" # Incorrect model = "gpt4o" # Missing hyphen model = "GPT-5.5" # Case sensitive ``` 2. **List available models:** ```bash curl https://api.apertis.ai/v1/models \ -H "Authorization: Bearer sk-your-api-key" ``` 3. **Check model availability:** - Some models may be temporarily unavailable - Check status page for outages #### Model Access Denied Your API key may not have access to the requested model: 1. Check model restrictions on your key 2. Verify your plan includes the model 3. Some models require special access ### Request Errors #### 400 Bad Request **Common causes:** 1. **Invalid JSON:** ```json // Wrong - trailing comma {"model": "gpt-5.5", "messages": [],} // Correct {"model": "gpt-5.5", "messages": []} ``` 2. **Missing required fields:** ```python # Wrong - missing messages client.chat.completions.create(model="gpt-5.5") # Correct client.chat.completions.create( model="gpt-5.5", messages=[{"role": "user", "content": "Hello"}] ) ``` 3. **Invalid parameter values:** ```python # Wrong - temperature out of range temperature=3.0 # Correct temperature=0.7 # Range: 0-2 ``` #### Request Timeout **Symptoms:** - Connection timeout - Read timeout - No response **Solutions:** 1. **Increase timeout settings:** ```python client = OpenAI( api_key="sk-xxx", base_url="https://api.apertis.ai/v1", timeout=120.0 # 120 seconds ) ``` 2. **Use streaming for long responses:** ```python response = client.chat.completions.create( model="gpt-5.5", messages=[...], stream=True # Enable streaming ) ``` 3. **Check for large payloads:** - Long prompts take longer to process - Consider breaking into smaller chunks ### Connection Issues #### SSL/TLS Errors **Symptoms:** ``` SSL: CERTIFICATE_VERIFY_FAILED ``` **Solutions:** 1. **Update SSL certificates:** ```bash # Python pip install --upgrade certifi # macOS /Applications/Python\ 3.x/Install\ Certificates.command ``` 2. **Check system time** - SSL validation requires correct time 3. **Corporate proxy issues:** - Configure proxy settings correctly - May need to add proxy CA certificates #### DNS Resolution Errors **Symptoms:** ``` Failed to resolve 'api.apertis.ai' ``` **Solutions:** 1. **Check internet connection** 2. **Try alternative DNS:** ```bash # Use Google DNS nslookup api.apertis.ai 8.8.8.8 ``` 3. **Flush DNS cache:** ```bash # macOS sudo dscacheutil -flushcache # Windows ipconfig /flushdns ``` ### Streaming Issues #### Incomplete Streaming Response **Symptoms:** - Stream stops mid-response - Missing content **Solutions:** 1. **Handle stream properly:** ```python full_response = "" for chunk in response: if chunk.choices[0].delta.content: full_response += chunk.choices[0].delta.content ``` 2. **Check for connection issues:** - Unstable network can interrupt streams - Consider retry logic for incomplete streams 3. **Increase read timeout:** ```python client = OpenAI( api_key="sk-xxx", base_url="https://api.apertis.ai/v1", timeout=httpx.Timeout(60.0, read=300.0) ) ``` ## Debugging Tools ### Enable Verbose Logging **Python:** ```python import logging import httpx logging.basicConfig(level=logging.DEBUG) httpx_logger = logging.getLogger("httpx") httpx_logger.setLevel(logging.DEBUG) ``` **cURL:** ```bash curl -v https://api.apertis.ai/v1/models \ -H "Authorization: Bearer sk-your-api-key" ``` ### Request/Response Inspection **Python:** ```python response = client.chat.completions.with_raw_response.create( model="gpt-5.5", messages=[{"role": "user", "content": "Hello"}] ) print(f"Status: {response.status_code}") print(f"Headers: {response.headers}") print(f"Body: {response.text}") ``` ## Environment-Specific Issues ### Python #### OpenAI SDK Version Ensure you're using a compatible SDK version: ```bash pip install --upgrade openai ``` Check version: ```python import openai print(openai.__version__) # Should be >= 1.0.0 ``` ### Node.js #### Package Version ```bash npm install openai@latest ``` #### ES Modules vs CommonJS ```javascript // ES Modules import OpenAI from 'openai'; // CommonJS const OpenAI = require('openai'); ``` ### Docker/Container #### DNS Issues in Containers Add DNS settings to your container: ```dockerfile # Dockerfile RUN echo "nameserver 8.8.8.8" > /etc/resolv.conf ``` Or in docker-compose: ```yaml services: app: dns: - 8.8.8.8 - 8.8.4.4 ``` ## Getting Help ### Before Contacting Support 1. **Check this troubleshooting guide** 2. **Review [Error Codes](/help/error-codes)** 3. **Search our documentation** 4. **Check [Status Page](https://status.apertis.ai)** ### Information to Include When contacting support, provide: - **Request ID** (from response headers if available) - **Timestamp** of the issue - **Error message** (full JSON response) - **Code snippet** (sanitize API key) - **Environment** (Python version, SDK version, OS) ### Contact Support - **Email:** hi@apertis.ai - **Dashboard:** Submit a ticket through the dashboard ## Related Topics - [Error Codes](/help/error-codes) - Complete error reference - [API Keys](/authentication/api-keys) - API key management - [Rate Limits](/billing/rate-limits) - Understanding rate limits --- Source: https://docs.apertis.ai/help/error-codes # Error Codes This reference documents all error codes returned by the Apertis API, along with their meanings and recommended solutions. ## Error Response Format All API errors follow this structure: ```json { "error": { "message": "Human-readable error description", "type": "error_category", "code": "specific_error_code", "param": "parameter_name" // Optional: which parameter caused the error } } ``` ## HTTP Status Codes ### Quick Reference | Status | Meaning | Action | |--------|---------|--------| | 200 | Success | Request completed | | 400 | Bad Request | Fix request parameters | | 401 | Unauthorized | Check API key | | 402 | Payment Required | Add quota/funds | | 403 | Forbidden | Check permissions | | 404 | Not Found | Check endpoint/model | | 429 | Too Many Requests | Reduce request rate | | 500 | Server Error | Retry or contact support | | 502 | Bad Gateway | Retry request | | 503 | Service Unavailable | Check status page | --- ## Authentication Errors (401) ### `invalid_api_key` ```json { "error": { "message": "Invalid API key provided", "type": "authentication_error", "code": "invalid_api_key" } } ``` **Causes:** - API key doesn't exist - API key was deleted - Typo in API key **Solutions:** 1. Verify key in dashboard 2. Create a new key if needed 3. Check for copy/paste errors ### `missing_api_key` ```json { "error": { "message": "API key is missing", "type": "authentication_error", "code": "missing_api_key" } } ``` **Solution:** Add the Authorization header: ```bash -H "Authorization: Bearer sk-your-api-key" ``` ### `expired_api_key` ```json { "error": { "message": "API key has expired", "type": "authentication_error", "code": "expired_api_key" } } ``` **Solution:** Create a new API key or extend the expiration date. --- ## Billing Errors (402) ### `quota_exceeded` ```json { "error": { "message": "You have exceeded your quota", "type": "billing_error", "code": "quota_exceeded" } } ``` **Causes:** - API key quota exhausted - Account balance depleted - Subscription quota used up **Solutions:** 1. Top up account balance 2. Enable PAYG fallback (for subscriptions) 3. Upgrade subscription plan 4. Wait for quota reset (subscription) ### `insufficient_balance` ```json { "error": { "message": "Insufficient account balance", "type": "billing_error", "code": "insufficient_balance" } } ``` **Solution:** Add funds to your account via the dashboard. ### `subscription_expired` ```json { "error": { "message": "Your subscription has expired", "type": "billing_error", "code": "subscription_expired" } } ``` **Solution:** Renew your subscription or switch to PAYG. ### `payment_failed` ```json { "error": { "message": "Payment processing failed", "type": "billing_error", "code": "payment_failed" } } ``` **Solutions:** 1. Check payment method is valid 2. Ensure sufficient funds on card 3. Try a different payment method --- ## Permission Errors (403) ### `access_denied` ```json { "error": { "message": "Access denied to this resource", "type": "permission_error", "code": "access_denied" } } ``` **Causes:** - Account suspended - Resource access restricted - IP not in whitelist ### `model_access_denied` ```json { "error": { "message": "Your API key does not have access to this model", "type": "permission_error", "code": "model_access_denied" } } ``` **Solutions:** 1. Check API key model restrictions 2. Use a different API key 3. Update key permissions ### `ip_not_allowed` ```json { "error": { "message": "Request from this IP is not allowed", "type": "permission_error", "code": "ip_not_allowed" } } ``` **Solution:** Add your IP address to the API key's whitelist. ### `account_suspended` ```json { "error": { "message": "Your account has been suspended", "type": "permission_error", "code": "account_suspended" } } ``` **Solution:** Contact support at hi@apertis.ai --- ## Not Found Errors (404) ### `model_not_found` ```json { "error": { "message": "The model 'xyz' does not exist", "type": "invalid_request_error", "code": "model_not_found" } } ``` **Solutions:** 1. Check model name spelling 2. List available models: `GET /v1/models` 3. Model may be deprecated ### `endpoint_not_found` ```json { "error": { "message": "The requested endpoint does not exist", "type": "invalid_request_error", "code": "endpoint_not_found" } } ``` **Solution:** Check the API endpoint path. --- ## Validation Errors (400) ### `invalid_request` ```json { "error": { "message": "Invalid request format", "type": "invalid_request_error", "code": "invalid_request" } } ``` **Common causes:** - Malformed JSON - Invalid Content-Type - Missing required fields ### `invalid_parameter` ```json { "error": { "message": "Invalid value for parameter 'temperature'", "type": "invalid_request_error", "code": "invalid_parameter", "param": "temperature" } } ``` **Solution:** Check parameter constraints: | Parameter | Valid Range | |-----------|-------------| | temperature | 0.0 - 2.0 | | top_p | 0.0 - 1.0 | | max_tokens | 1 - model limit | | n | 1 - 128 | ### `missing_parameter` ```json { "error": { "message": "Missing required parameter 'messages'", "type": "invalid_request_error", "code": "missing_parameter", "param": "messages" } } ``` **Solution:** Include all required parameters in your request. ### `context_length_exceeded` ```json { "error": { "message": "Maximum context length exceeded", "type": "invalid_request_error", "code": "context_length_exceeded" } } ``` **Solutions:** 1. Reduce prompt length 2. Use a model with larger context 3. Summarize previous messages | Model | Context Limit | |-------|---------------| | GPT-3.5 Turbo | 16K tokens | | GPT-5.5 | 128K tokens | | Claude Sonnet 4.6 | 200K tokens | ### `invalid_image` ```json { "error": { "message": "Invalid image format or size", "type": "invalid_request_error", "code": "invalid_image" } } ``` **Solutions:** 1. Use supported formats (PNG, JPEG, WebP, GIF) 2. Reduce image size (max 20MB) 3. Check base64 encoding --- ## Rate Limit Errors (429) ### `rate_limit_exceeded` ```json { "error": { "message": "Rate limit exceeded. Please wait before making more requests.", "type": "rate_limit_error", "code": "rate_limit_exceeded" } } ``` **Response Headers:** ``` X-RateLimit-Limit: 1500 X-RateLimit-Remaining: 0 X-RateLimit-Reset: 1703894400 Retry-After: 45 ``` **Solutions:** 1. Wait for `Retry-After` seconds 2. Implement exponential backoff 3. Reduce request frequency 4. Upgrade plan for higher limits --- ## Server Errors (5xx) ### `internal_error` (500) ```json { "error": { "message": "An internal server error occurred", "type": "server_error", "code": "internal_error" } } ``` **Solutions:** 1. Retry the request 2. Check status page 3. Contact support if persistent ### `service_unavailable` (503) ```json { "error": { "message": "Service temporarily unavailable", "type": "server_error", "code": "service_unavailable" } } ``` **Solutions:** 1. Wait a few minutes 2. Check status page 3. Retry with exponential backoff ### `model_overloaded` (503) ```json { "error": { "message": "The model is currently overloaded", "type": "server_error", "code": "model_overloaded" } } ``` **Solutions:** 1. Use fallback models 2. Retry after a delay 3. Try a different model ### `upstream_error` (502) ```json { "error": { "message": "Error communicating with upstream provider", "type": "server_error", "code": "upstream_error" } } ``` **Solutions:** 1. Retry the request 2. Enable fallback models 3. Check provider status --- ## Model-Specific Errors ### `content_policy_violation` ```json { "error": { "message": "Content violates usage policies", "type": "content_policy_error", "code": "content_policy_violation" } } ``` **Solution:** Review and modify your prompt to comply with usage policies. ### `output_length_exceeded` ```json { "error": { "message": "Output exceeds maximum length", "type": "invalid_request_error", "code": "output_length_exceeded" } } ``` **Solution:** Set a lower `max_tokens` value or request shorter responses. --- ## Error Handling Best Practices ### Python Example ```python from openai import OpenAI, APIError, RateLimitError, AuthenticationError client = OpenAI( api_key="sk-your-api-key", base_url="https://api.apertis.ai/v1" ) try: response = client.chat.completions.create( model="gpt-5.5", messages=[{"role": "user", "content": "Hello"}] ) except AuthenticationError as e: print(f"Authentication failed: {e}") # Check API key except RateLimitError as e: print(f"Rate limited: {e}") # Implement retry with backoff except APIError as e: print(f"API error: {e.status_code} - {e.message}") # Handle other errors ``` ### Node.js Example ```javascript import OpenAI from 'openai'; const client = new OpenAI({ apiKey: 'sk-your-api-key', baseURL: 'https://api.apertis.ai/v1' }); try { const response = await client.chat.completions.create({ model: 'gpt-5.5', messages: [{ role: 'user', content: 'Hello' }] }); } catch (error) { if (error.status === 401) { console.error('Authentication failed'); } else if (error.status === 429) { console.error('Rate limited, retrying...'); } else if (error.status >= 500) { console.error('Server error, please retry'); } else { console.error(`Error: ${error.message}`); } } ``` ### Retry with Exponential Backoff ```python import time import random def make_request_with_retry(func, max_retries=5): for attempt in range(max_retries): try: return func() except RateLimitError: if attempt == max_retries - 1: raise wait_time = (2 ** attempt) + random.uniform(0, 1) time.sleep(wait_time) except APIError as e: if e.status_code >= 500 and attempt < max_retries - 1: time.sleep(2 ** attempt) continue raise ``` --- ## Related Topics - [Troubleshooting](/help/troubleshooting) - Common issues and solutions - [Rate Limits](/billing/rate-limits) - Understanding rate limits - [API Keys](/authentication/api-keys) - API key management --- Source: https://docs.apertis.ai/help/migration-guides # Migration Guides This guide helps you migrate to Apertis from other API providers or update between API versions. ## Migrating from OpenAI Apertis is fully compatible with the OpenAI API format, making migration straightforward. ### Step 1: Update Base URL The only required change is updating the base URL: **Python:** ```python from openai import OpenAI # Before (OpenAI) client = OpenAI( api_key="sk-openai-key" ) # After (Apertis) client = OpenAI( api_key="sk-apertis-key", base_url="https://api.apertis.ai/v1" ) ``` **Node.js:** ```javascript import OpenAI from 'openai'; // Before (OpenAI) const client = new OpenAI({ apiKey: 'sk-openai-key' }); // After (Apertis) const client = new OpenAI({ apiKey: 'sk-apertis-key', baseURL: 'https://api.apertis.ai/v1' }); ``` **cURL:** ```bash # Before (OpenAI) curl https://api.openai.com/v1/chat/completions \ -H "Authorization: Bearer sk-openai-key" \ ... # After (Apertis) curl https://api.apertis.ai/v1/chat/completions \ -H "Authorization: Bearer sk-apertis-key" \ ... ``` ### Step 2: Get Your Apertis API Key 1. Sign up at [api.apertis.ai](https://api.apertis.ai) 2. Navigate to **API Keys** 3. Create a new API key 4. Store it securely ### Step 3: Update Environment Variables ```bash # Before export OPENAI_API_KEY="sk-openai-key" # After export APERTIS_API_KEY="sk-apertis-key" export APERTIS_BASE_URL="https://api.apertis.ai/v1" ``` ### Step 4: Test Your Integration ```python from openai import OpenAI import os client = OpenAI( api_key=os.environ.get("APERTIS_API_KEY"), base_url=os.environ.get("APERTIS_BASE_URL") ) # Test with a simple request response = client.chat.completions.create( model="gpt-5.5", messages=[{"role": "user", "content": "Hello!"}] ) print(response.choices[0].message.content) ``` ### Model Name Mapping Most OpenAI models are available with the same names: | OpenAI Model | Apertis Model | Notes | |--------------|---------------|-------| | gpt-5.5 | gpt-5.5 | ✓ Same | | gpt-5.4-mini | gpt-5.4-mini | ✓ Same | | gpt-5.4-nano | gpt-5.4-nano | ✓ Same | | text-embedding-3-small | jina-embeddings-v3 | Different model (1024 dimensions); re-embed stored vectors | | whisper-1 | whisper-1 | ✓ Same | | tts-1 | gpt-4o-mini-tts-2025-12-15 | Same `/v1/audio/speech` request shape | | dall-e-3 | gpt-image-2 | Same `/v1/images/generations` endpoint; returns base64 images | ### Additional Models Apertis provides access to models not available directly from OpenAI: ```python # Use Claude models response = client.chat.completions.create( model="claude-sonnet-4-6", messages=[{"role": "user", "content": "Hello!"}] ) # Use Gemini models response = client.chat.completions.create( model="gemini-3.1-pro-preview", messages=[{"role": "user", "content": "Hello!"}] ) ``` ## Migrating from Anthropic (Claude Direct) ### API Format Differences Anthropic uses a different API format. Here's how to migrate: **Before (Anthropic SDK):** ```python import anthropic client = anthropic.Anthropic(api_key="sk-ant-xxx") message = client.messages.create( model="claude-sonnet-4-6", max_tokens=1024, messages=[ {"role": "user", "content": "Hello!"} ] ) ``` **After (Apertis with OpenAI format):** ```python from openai import OpenAI client = OpenAI( api_key="sk-apertis-key", base_url="https://api.apertis.ai/v1" ) response = client.chat.completions.create( model="claude-sonnet-4-6", max_tokens=1024, messages=[ {"role": "user", "content": "Hello!"} ] ) ``` ### Key Differences | Feature | Anthropic API | Apertis API | |---------|--------------|-------------| | SDK | anthropic | openai | | Response format | `message.content[0].text` | `choices[0].message.content` | | System prompt | `system` parameter | System message in `messages` | | Streaming | Different format | OpenAI SSE format | ### System Prompts **Anthropic format:** ```python message = client.messages.create( model="claude-sonnet-4-6", system="You are a helpful assistant.", messages=[{"role": "user", "content": "Hello!"}] ) ``` **Apertis format:** ```python response = client.chat.completions.create( model="claude-sonnet-4-6", messages=[ {"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": "Hello!"} ] ) ``` ### Response Parsing **Anthropic:** ```python text = message.content[0].text ``` **Apertis:** ```python text = response.choices[0].message.content ``` ## Migrating from Azure OpenAI ### Configuration Changes **Before (Azure OpenAI):** ```python from openai import AzureOpenAI client = AzureOpenAI( api_key="azure-api-key", api_version="2024-02-15-preview", azure_endpoint="https://your-resource.openai.azure.com" ) response = client.chat.completions.create( model="your-deployment-name", # Deployment name, not model messages=[{"role": "user", "content": "Hello!"}] ) ``` **After (Apertis):** ```python from openai import OpenAI client = OpenAI( api_key="sk-apertis-key", base_url="https://api.apertis.ai/v1" ) response = client.chat.completions.create( model="gpt-5.5", # Actual model name messages=[{"role": "user", "content": "Hello!"}] ) ``` ### Key Differences | Feature | Azure OpenAI | Apertis | |---------|--------------|---------| | Client | AzureOpenAI | OpenAI | | Model param | Deployment name | Model name | | API versioning | Required | Not needed | | Endpoint | Custom per resource | Fixed | ## Migrating from Google (Vertex AI / Gemini) ### From Vertex AI SDK **Before (Vertex AI):** ```python import vertexai from vertexai.generative_models import GenerativeModel vertexai.init(project="your-project", location="us-central1") model = GenerativeModel("gemini-3.1-pro-preview") response = model.generate_content("Hello!") ``` **After (Apertis):** ```python from openai import OpenAI client = OpenAI( api_key="sk-apertis-key", base_url="https://api.apertis.ai/v1" ) response = client.chat.completions.create( model="gemini-3.1-pro-preview", messages=[{"role": "user", "content": "Hello!"}] ) ``` ### From Google AI Studio **Before (Google AI SDK):** ```python import google.generativeai as genai genai.configure(api_key="google-api-key") model = genai.GenerativeModel("gemini-3.1-pro-preview") response = model.generate_content("Hello!") text = response.text ``` **After (Apertis):** ```python from openai import OpenAI client = OpenAI( api_key="sk-apertis-key", base_url="https://api.apertis.ai/v1" ) response = client.chat.completions.create( model="gemini-3.1-pro-preview", messages=[{"role": "user", "content": "Hello!"}] ) text = response.choices[0].message.content ``` ## Migrating from Other Providers ### LiteLLM If you're using LiteLLM, you can switch to Apertis: **Before (LiteLLM):** ```python from litellm import completion response = completion( model="gpt-5.5", messages=[{"role": "user", "content": "Hello!"}] ) ``` **After (Apertis):** ```python from openai import OpenAI client = OpenAI( api_key="sk-apertis-key", base_url="https://api.apertis.ai/v1" ) response = client.chat.completions.create( model="gpt-5.5", messages=[{"role": "user", "content": "Hello!"}] ) ``` ### OpenRouter **Before (OpenRouter):** ```python from openai import OpenAI client = OpenAI( api_key="sk-or-xxx", base_url="https://openrouter.ai/api/v1" ) ``` **After (Apertis):** ```python from openai import OpenAI client = OpenAI( api_key="sk-apertis-key", base_url="https://api.apertis.ai/v1" ) ``` ## Framework Migrations ### LangChain ```python from langchain_openai import ChatOpenAI # Before llm = ChatOpenAI(model="gpt-5.5") # After llm = ChatOpenAI( model="gpt-5.5", openai_api_key="sk-apertis-key", openai_api_base="https://api.apertis.ai/v1" ) ``` ### LlamaIndex ```python from llama_index.llms.openai import OpenAI # Before llm = OpenAI(model="gpt-5.5") # After llm = OpenAI( model="gpt-5.5", api_key="sk-apertis-key", api_base="https://api.apertis.ai/v1" ) ``` ## Migration Checklist ### Pre-Migration - [ ] Sign up for Apertis account - [ ] Create API key - [ ] Review current API usage patterns - [ ] Identify all integration points - [ ] Plan testing strategy ### During Migration - [ ] Update base URL/endpoint - [ ] Update API key - [ ] Update environment variables - [ ] Update model names if needed - [ ] Test each integration point ### Post-Migration - [ ] Verify all features work correctly - [ ] Monitor error rates - [ ] Check response quality - [ ] Review billing/usage - [ ] Update documentation ## Troubleshooting Migration Issues ### Common Issues | Issue | Cause | Solution | |-------|-------|----------| | 401 Unauthorized | Wrong API key | Use Apertis key, not old provider key | | Model not found | Different model name | Check [available models](/installation/models) | | Different response format | SDK mismatch | Use OpenAI SDK format | | Missing features | Not all features supported | Check documentation for alternatives | ### Getting Help If you encounter issues during migration: 1. Check the [Troubleshooting Guide](/help/troubleshooting) 2. Review [Error Codes](/help/error-codes) 3. Contact support at hi@apertis.ai ## Related Topics - [Quick Start](/getting-started/quick-start) - Get started with Apertis - [API Keys](/authentication/api-keys) - Key management - [Models](/installation/models) - Available models - [FAQ](/help/faq) - Common questions --- Source: https://docs.apertis.ai/opensource # Open-Source Models ## Open-Source Model Services At Apertis AI, we specialize in offering a variety of cutting-edge open-source large language model services. These models, developed by globally leading technology companies, are carefully fine-tuned and optimized by our team to meet the unique requirements of diverse clients. ### Llama Models The Llama series, developed by Meta (formerly Facebook), is one of the most advanced open-source language models available on the market. These models possess robust natural language processing capabilities, capable of understanding and generating high-quality textual content. They are applicable to a wide range of scenarios, including text analysis, automatic summarization, and language translation. Our expert team has optimized the Llama-3.2 and Llama-3.1 models to ensure efficient performance across various business environments. ### Gemma Models The Gemma models, developed by Google, are also leading open-source language models with exceptional language understanding and generation capabilities. Trained on large-scale datasets, the Gemma models excel in handling complex language tasks, such as conversational systems, content generation, and information retrieval. Apertis AI is committed to integrating this powerful tool into diverse business scenarios, enabling clients to implement AI-driven solutions. ### Qwen Models The Qwen series, introduced by Alibaba, is among the most advanced open-source language models on the market. These models exhibit strong natural language processing capabilities, enabling them to understand and generate high-quality textual content. They are ideal for applications such as text analysis, automatic summarization, and language translation. Our professional team has optimized the Qwen-2.5 and Qwen-2.0 models to ensure their efficient operation in a variety of business environments. ### Mistral Models Developed by Mistral AI, the Mistral models are high-performance open-source language models. These models excel in multiple language tasks, including language understanding, text generation, and language translation. Apertis AI is dedicated to introducing this powerful tool into various business scenarios to help clients achieve AI-powered solutions. ## Model List For a comprehensive list of models we offer, please visit: [**Apertis Model List**](https://apertis.ai/models) --- Source: https://docs.apertis.ai/stimachat # Web Chat Service **[Chat](https://chat.apertis.ai)** is an advanced conversation system developed by Apertis AI, featuring a **bidirectional multi-turn dialogue** mode and supporting interactions with **100+ large language models**. ## Features - **Bidirectional Multi-Turn Dialogue**: Chat allows users to engage in bidirectional multi-turn conversations, not only answering questions but also proactively asking them. - **Multilingual Support**: Chat supports communication in multiple languages, including Chinese, English, Japanese, Korean, and more. - **Advanced conversation Technology**: Chat leverages cutting-edge conversation technologies, including **semantic understanding**, **sentiment analysis**, and **intent recognition**, to better understand user intent and emotions. ## How to Use ### Access the Conversation Interface Once you log in and create an API Key, each key will have a blue **[Chat Service]** button next to it. Click this button to use the corresponding API Key for chatting. ![](/assets/images/stimachat_1-122ac29e62fe1399.png) After entering, the system will automatically apply the API Key and endpoint. ![](/assets/images/stimachat_2-d21852ecd493e427.png) ### Start Chatting Once in the interface, click the **[New Chat]** button at the bottom. ![](/assets/images/stimachat_3-20c3362ef5256bcc.png) Then, select the AI model by clicking on the robot icon at the bottom. ![](/assets/images/stimachat_4-735a0c9f7b9df5d3.png) You can now begin using the system. --- Source: https://docs.apertis.ai/api/ # API Reference Welcome to the Apertis API Reference. This documentation covers all available API endpoints, SDKs, and integration guides. ## Choosing an API format The gateway accepts text generation requests in three formats: Chat Completions, Responses, and Messages. If you're not sure which to use, start with Chat Completions — it's the format the Python SDK's own quick start uses. | | Chat Completions | Responses | Messages | |---|---|---|---| | Endpoint path | `/v1/chat/completions` | `/v1/responses` | `/v1/messages` | | Compatible SDK | OpenAI SDK (`chat.completions.create`, `base_url` ends in `/v1`) or the Apertis SDK | OpenAI SDK (`responses.create`, `base_url` ends in `/v1`) or the Apertis SDK | Anthropic SDK (`messages.create`, `base_url` has no `/v1`) or the Apertis SDK | | Best for | OpenAI-compatible chat API | Models that only support `/v1/responses` (e.g. `gpt-5-pro`, `o1-pro`, `codex-mini`); built-in tools; stateful multi-turn | Claude models only, via Anthropic-type channels | | Streaming | `stream: true` | `stream: true` | `stream: true`, with `message_start` / `content_block_delta` / `message_stop` SSE events | | Tool calling | `tools`, `tool_choice`, `parallel_tool_calls` | `tools`, `tool_choice`, `max_tool_calls`, `parallel_tool_calls`, plus built-in `web_search`, `code_interpreter`, `file_search` | `tools`, `tool_choice` | | Reasoning / thinking | `reasoning_effort` string; Claude `thinking` via `extra_body`; DeepSeek returns `reasoning_content` | `reasoning` object (`effort`, `summary`) | `thinking` object (`type`, `budget_tokens`) | | Prompt caching | See [Prompt Cache](/api/text-generation/prompt-cache) | See [Prompt Cache](/api/text-generation/prompt-cache) | See [Prompt Cache](/api/text-generation/prompt-cache) | | Structured output | `response_format` with `json_schema` | `text.format` | `response_format` (OpenAI-compatible extended parameter) | - Pick [Chat Completions](/api/text-generation/chat-completions) if you're already using an OpenAI-compatible client and the standard `messages` array. - Pick [Responses](/api/text-generation/responses) if your model only supports `/v1/responses`, or you need built-in tools or stateful multi-turn via `previous_response_id`. - Pick [Messages](/api/text-generation/messages) if you're calling Claude models directly and want the native Anthropic SDK and extended thinking. ## Quick Links ### Text Generation - [Chat Completions](/api/text-generation/chat-completions) - OpenAI-compatible chat API - [Responses API](/api/text-generation/responses) - OpenAI Responses format - [Messages API](/api/text-generation/messages) - Anthropic Messages format ### Multimodal - [Vision](/api/vision/read-image) - Image understanding - [Image Generation](/api/vision/image-generation) - Create images with AI - [Audio](/api/audio-video/audio) - Speech-to-text and text-to-speech - [Video](/api/audio-video/video) - Video generation (not currently available) ### Utilities - [Billing Credits](/api/utilities/billing-credits) - Check remaining credits and subscription quota ### SDKs & Libraries - [Python SDK](/api/sdks/python-sdk) - Official Python client - [AI SDK Provider](/api/sdks/ai-sdk-provider) - Vercel AI SDK integration - [LangChain](/api/sdks/langchain) - LangChain integration - [LlamaIndex](/api/sdks/llamaindex) - LlamaIndex integration - [LiteLLM](/api/sdks/litellm) - LiteLLM proxy support ## Base URL All API requests should be made to: ``` https://api.apertis.ai/v1 ``` ## Authentication Include your API key in the `Authorization` header: ```bash Authorization: Bearer YOUR_API_KEY ``` See [API Keys](/authentication/api-keys) for details on obtaining and managing your API keys. --- Source: https://docs.apertis.ai/api/text-generation/chat-completions # Chat Completion ```json /v1/chat/completions ``` ## HTTP Request ```bash tab="cURL" curl https://api.apertis.ai/v1/chat/completions \ -H "Content-Type: application/json" \ -H "Authorization: Bearer " \ -d '{ "model": "", "messages": [ { "role": "system", "content": "" } ] }' ``` ```python tab="Python" from openai import OpenAI client = OpenAI( api_key="", base_url="https://api.apertis.ai/v1" ) response = client.chat.completions.create( model="", messages=[ { "role": "system", "content": "" } ] ) print(response.choices[0].message.content) ``` ```javascript tab="JavaScript" import OpenAI from 'openai'; const client = new OpenAI({ apiKey: '', baseURL: 'https://api.apertis.ai/v1' }); const response = await client.chat.completions.create({ model: '', messages: [ { role: 'system', content: '' } ] }); console.log(response.choices[0].message.content); ``` - ``: Your API key - ``: The alias of the model to use - ``: The messages to send to the model ## Optional Headers | Header | Type | Description | |--------|------|-------------| | `X-Timeout` | integer | Total request timeout in milliseconds. Caps the entire request lifecycle including all channel retries. Range: 5,000–300,000 ms. Default: 60,000 ms (60s). See [Request Timeout](#request-timeout). | ## Optional Parameters | Parameter | Type | Description | |-----------|------|-------------| | `temperature` | number | Sampling temperature (0-2). Default: 1 | | `max_tokens` | integer | Maximum tokens in the response | | `top_p` | number | Nucleus sampling threshold (0-1) | | `stream` | boolean | Enable streaming. Default: false | | `compression` | object | [Context compression](/api/text-generation/context-compression) configuration | ### Context Compression Add a `compression` object to automatically summarize older conversation history and reduce token usage for long conversations: ```bash tab="cURL" curl https://api.apertis.ai/v1/chat/completions \ -H "Content-Type: application/json" \ -H "Authorization: Bearer " \ -d '{ "model": "gemini-3-flash-preview", "messages": [{"role": "user", "content": "Hello!"}], "compression": {"enabled": true, "model": "gemini-3-flash-preview"} }' ``` ```python tab="Python" from openai import OpenAI client = OpenAI( api_key="", base_url="https://api.apertis.ai/v1" ) response = client.chat.completions.create( model="gemini-3-flash-preview", messages=[{"role": "user", "content": "Hello!"}], extra_body={"compression": {"enabled": True, "model": "gemini-3-flash-preview"}} ) ``` ```javascript tab="JavaScript" import OpenAI from 'openai'; const client = new OpenAI({ apiKey: '', baseURL: 'https://api.apertis.ai/v1' }); const response = await client.chat.completions.create({ model: 'gemini-3-flash-preview', messages: [{ role: 'user', content: 'Hello!' }], compression: { enabled: true, model: 'gemini-3-flash-preview' } }); ``` See [Context Compression](/api/text-generation/context-compression) for full documentation. ### Request Timeout Use the `X-Timeout` header to limit the total time a request can take, including all internal channel retries. This prevents long waits when multiple upstream providers are slow or unavailable. ```bash tab="cURL" curl https://api.apertis.ai/v1/chat/completions \ -H "Content-Type: application/json" \ -H "Authorization: Bearer " \ -H "X-Timeout: 20000" \ -d '{ "model": "gemini-3-flash-preview", "messages": [{"role": "user", "content": "Hello!"}] }' ``` ```python tab="Python" from openai import OpenAI client = OpenAI( api_key="your-apertis-key", base_url="https://api.apertis.ai/v1" ) response = client.chat.completions.create( model="gemini-3-flash-preview", messages=[{"role": "user", "content": "Hello!"}], extra_headers={"X-Timeout": "20000"} # 20 seconds ) ``` ```javascript tab="JavaScript" import OpenAI from 'openai'; const client = new OpenAI({ apiKey: 'your-apertis-key', baseURL: 'https://api.apertis.ai/v1' }); const response = await client.chat.completions.create( { model: 'gemini-3-flash-preview', messages: [{ role: 'user', content: 'Hello!' }] }, { headers: { 'X-Timeout': '20000' } } // 20 seconds ); ``` | Setting | Value | |---------|-------| | Default | 60,000 ms (60 seconds) | | Minimum | 5,000 ms (5 seconds) | | Maximum | 300,000 ms (5 minutes) | When the timeout is exceeded, the API returns **HTTP 408** with diagnostic headers: | Header | Description | |--------|-------------| | `X-Timeout-Attempts` | Number of upstream channels attempted | | `X-Timeout-Elapsed-Ms` | Actual elapsed time in milliseconds | ```json { "error": { "message": "Total request timeout (20000ms) exceeded after 3 channel attempts. Set X-Timeout header to adjust.", "type": "timeout", "code": "request_timeout" } } ``` > **Tip: Streaming & Thinking Models** > > For streaming requests, the timeout only applies to the initial connection phase. Once the first response chunk arrives, the timer is stopped and the stream can run as long as needed. This ensures thinking models (e.g., `claude-opus-4-6`) are not interrupted mid-generation. See also: [Fallback Models](/api/utilities/fallback-models) for per-model fallback timeout configuration. --- Source: https://docs.apertis.ai/api/text-generation/responses # Responses API ``` POST /v1/responses ``` The Responses API is compatible with OpenAI's Responses API format, providing a streamlined interface for generating AI responses. This endpoint accepts the `input` field (instead of `messages`) and automatically converts it to the internal format. > **Info: Input Format** > > The Responses API uses `input` instead of `messages`. You can provide either a string for simple queries or an array of message objects for conversations. ## HTTP Request ```bash tab="cURL" curl https://api.apertis.ai/v1/responses \ -H "Content-Type: application/json" \ -H "Authorization: Bearer " \ -d '{ "model": "gpt-5.2", "input": "What is the capital of France?" }' ``` ```python tab="Python" from openai import OpenAI client = OpenAI( api_key="", base_url="https://api.apertis.ai/v1" ) response = client.responses.create( model="gpt-5.2", input="What is the capital of France?" ) print(response.output_text) ``` ```javascript tab="JavaScript" import OpenAI from 'openai'; const client = new OpenAI({ apiKey: '', baseURL: 'https://api.apertis.ai/v1' }); const response = await client.responses.create({ model: 'gpt-5.2', input: 'What is the capital of France?' }); console.log(response.output_text); ``` ## Authentication | Header | Format | Example | |--------|--------|---------| | `Authorization` | Bearer token | `Authorization: Bearer sk-your-api-key` | ## Parameters ### Required Parameters | Parameter | Type | Description | |-----------|------|-------------| | `model` | string | The model to use for generating the response | | `input` | string/array | The input text or array of message objects | ### Optional Parameters | Parameter | Type | Description | |-----------|------|-------------| | `instructions` | string | High-level instructions for model behavior | | `stream` | boolean | Enable streaming responses. Default: false | | `temperature` | number | Sampling temperature (0-2). Default: 1 | | `max_tokens` | integer | Maximum tokens in the response | | `max_output_tokens` | integer | Upper bound for tokens including reasoning tokens | | `tools` | array | List of tools the model can use (including web search) | | `tool_choice` | string/object | Controls tool selection behavior | | `max_tool_calls` | integer | Maximum number of tool calls allowed | | `parallel_tool_calls` | boolean | Whether to allow parallel tool execution | | `reasoning` | object | Reasoning configuration (see below) | | `text` | object | Text output configuration (see below) | | `store` | boolean | Whether to store the response for state management | | `previous_response_id` | string | ID of previous response for multi-turn conversations | | `truncation` | string | Context overflow handling (e.g., `"auto"`) | | `metadata` | object | Request metadata (up to 16 key-value pairs) | ### Reasoning Parameter The `reasoning` parameter configures the model's reasoning behavior: | Option | Type | Description | |--------|------|-------------| | `effort` | string | Reasoning effort level: `low`, `medium`, `high` | | `summary` | string | Summary style: `auto`, `concise`, `detailed` | ```bash tab="cURL" curl https://api.apertis.ai/v1/responses \ -H "Content-Type: application/json" \ -H "Authorization: Bearer " \ -d '{ "model": "gpt-5.2", "input": "Solve this complex math problem...", "reasoning": {"effort": "high", "summary": "detailed"} }' ``` ```python tab="Python" response = client.responses.create( model="gpt-5.2", input="Solve this complex math problem...", reasoning={ "effort": "high", "summary": "detailed" } ) ``` ```javascript tab="JavaScript" const response = await client.responses.create({ model: 'gpt-5.2', input: 'Solve this complex math problem...', reasoning: { effort: 'high', summary: 'detailed' } }); ``` ### Text Parameter The `text` parameter configures text output: | Option | Type | Description | |--------|------|-------------| | `format` | object | Output format configuration (e.g., `{"type": "json_object"}`) | | `verbosity` | string | Output verbosity: `concise`, `normal`, `verbose` | ### Input Formats **String Input** (simple query): ```json { "model": "gpt-5.5", "input": "What is the capital of France?" } ``` **Array Input** (conversation): ```json { "model": "gpt-5.5", "input": [ {"role": "user", "content": "Hello!"}, {"role": "assistant", "content": "Hi there!"}, {"role": "user", "content": "What's 2+2?"} ] } ``` ## Example Usage ### Python ```python from openai import OpenAI client = OpenAI( api_key="sk-your-api-key", base_url="https://api.apertis.ai/v1" ) response = client.responses.create( model="gpt-5.5", input="Explain quantum computing in simple terms." ) print(response.output_text) ``` ### JavaScript ```javascript import OpenAI from 'openai'; const client = new OpenAI({ apiKey: 'sk-your-api-key', baseURL: 'https://api.apertis.ai/v1' }); const response = await client.responses.create({ model: 'gpt-5.5', input: 'Explain quantum computing in simple terms.' }); console.log(response.output_text); ``` ### With Web Search Use the `tools` parameter to enable web search: ```bash tab="cURL" curl https://api.apertis.ai/v1/responses \ -H "Content-Type: application/json" \ -H "Authorization: Bearer " \ -d '{ "model": "gpt-5.2", "input": "What are the latest news about AI?", "tools": [{"type": "web_search_preview"}] }' ``` ```python tab="Python" response = client.responses.create( model="gpt-5.2", input="What are the latest news about AI?", tools=[{"type": "web_search_preview"}] ) ``` ```javascript tab="JavaScript" const response = await client.responses.create({ model: 'gpt-5.2', input: 'What are the latest news about AI?', tools: [{ type: 'web_search_preview' }] }); ``` ### With Reasoning ```bash tab="cURL" curl https://api.apertis.ai/v1/responses \ -H "Content-Type: application/json" \ -H "Authorization: Bearer " \ -d '{ "model": "gpt-5.2", "input": "Prove the Pythagorean theorem step by step.", "reasoning": {"effort": "high", "summary": "detailed"} }' ``` ```python tab="Python" response = client.responses.create( model="gpt-5.2", input="Prove the Pythagorean theorem step by step.", reasoning={ "effort": "high", "summary": "detailed" } ) ``` ```javascript tab="JavaScript" const response = await client.responses.create({ model: 'gpt-5.2', input: 'Prove the Pythagorean theorem step by step.', reasoning: { effort: 'high', summary: 'detailed' } }); ``` ### Streaming ```bash tab="cURL" curl https://api.apertis.ai/v1/responses \ -H "Content-Type: application/json" \ -H "Authorization: Bearer " \ -d '{ "model": "gpt-5.2", "input": "Write a short story about a robot.", "stream": true }' ``` ```python tab="Python" stream = client.responses.create( model="gpt-5.2", input="Write a short story about a robot.", stream=True ) for event in stream: if event.type == "response.output_text.delta": print(event.delta, end="", flush=True) ``` ```javascript tab="JavaScript" const stream = await client.responses.create({ model: 'gpt-5.2', input: 'Write a short story about a robot.', stream: true }); for await (const event of stream) { if (event.type === 'response.output_text.delta') process.stdout.write(event.delta); } ``` ### Multi-turn Conversation ```bash tab="cURL" curl https://api.apertis.ai/v1/responses \ -H "Content-Type: application/json" \ -H "Authorization: Bearer " \ -d '{ "model": "gpt-5.2", "input": [ {"role": "user", "content": "What is Python?"}, {"role": "assistant", "content": "Python is a high-level programming language..."}, {"role": "user", "content": "How do I install it?"} ] }' ``` ```python tab="Python" response = client.responses.create( model="gpt-5.2", input=[ {"role": "user", "content": "What is Python?"}, {"role": "assistant", "content": "Python is a high-level programming language..."}, {"role": "user", "content": "How do I install it?"} ] ) ``` ```javascript tab="JavaScript" const response = await client.responses.create({ model: 'gpt-5.2', input: [ { role: 'user', content: 'What is Python?' }, { role: 'assistant', content: 'Python is a high-level programming language...' }, { role: 'user', content: 'How do I install it?' } ] }); ``` ### With Instructions Use `instructions` to provide high-level guidance for model behavior: ```bash tab="cURL" curl https://api.apertis.ai/v1/responses \ -H "Content-Type: application/json" \ -H "Authorization: Bearer " \ -d '{ "model": "gpt-5.2", "instructions": "You are a helpful coding assistant. Always provide code examples.", "input": "How do I read a file in Python?" }' ``` ```python tab="Python" response = client.responses.create( model="gpt-5.2", instructions="You are a helpful coding assistant. Always provide code examples.", input="How do I read a file in Python?" ) ``` ```javascript tab="JavaScript" const response = await client.responses.create({ model: 'gpt-5.2', instructions: 'You are a helpful coding assistant. Always provide code examples.', input: 'How do I read a file in Python?' }); ``` ### Stateful Conversations Use `store` and `previous_response_id` for multi-turn conversations with state: ```bash tab="cURL" curl https://api.apertis.ai/v1/responses \ -H "Content-Type: application/json" \ -H "Authorization: Bearer " \ -d '{ "model": "gpt-5.2", "input": "My name is Alice.", "store": true }' # Follow-up request - reference the previous response curl https://api.apertis.ai/v1/responses \ -H "Content-Type: application/json" \ -H "Authorization: Bearer " \ -d '{ "model": "gpt-5.2", "input": "What is my name?", "previous_response_id": "" }' ``` ```python tab="Python" # First request - store the response response1 = client.responses.create( model="gpt-5.2", input="My name is Alice.", store=True ) # Follow-up request - reference the previous response response2 = client.responses.create( model="gpt-5.2", input="What's my name?", previous_response_id=response1.id ) ``` ```javascript tab="JavaScript" // First request - store the response const response1 = await client.responses.create({ model: 'gpt-5.2', input: 'My name is Alice.', store: true }); // Follow-up request - reference the previous response const response2 = await client.responses.create({ model: 'gpt-5.2', input: "What's my name?", previous_response_id: response1.id }); ``` ### With Function Calling ```bash tab="cURL" curl https://api.apertis.ai/v1/responses \ -H "Content-Type: application/json" \ -H "Authorization: Bearer " \ -d '{ "model": "gpt-5.2", "input": "What is the weather in Tokyo?", "tools": [ { "type": "function", "function": { "name": "get_weather", "description": "Get the current weather", "parameters": { "type": "object", "properties": { "location": {"type": "string"} }, "required": ["location"] } } } ], "tool_choice": "auto" }' ``` ```python tab="Python" tools = [ { "type": "function", "function": { "name": "get_weather", "description": "Get the current weather", "parameters": { "type": "object", "properties": { "location": {"type": "string"} }, "required": ["location"] } } } ] response = client.responses.create( model="gpt-5.2", input="What's the weather in Tokyo?", tools=tools, tool_choice="auto" ) ``` ```javascript tab="JavaScript" const tools = [ { type: 'function', function: { name: 'get_weather', description: 'Get the current weather', parameters: { type: 'object', properties: { location: { type: 'string' } }, required: ['location'] } } } ]; const response = await client.responses.create({ model: 'gpt-5.2', input: "What's the weather in Tokyo?", tools, tool_choice: 'auto' }); ``` ## Response Format ```json { "id": "resp_abc123", "object": "response", "created_at": 1699000000, "model": "gpt-5.5", "output": [ { "type": "message", "role": "assistant", "content": [ { "type": "output_text", "text": "The capital of France is Paris." } ] } ], "usage": { "input_tokens": 12, "output_tokens": 8, "total_tokens": 20 } } ``` ## Supported Models All chat models available through Apertis are supported with the Responses API. The API intelligently routes requests based on model capabilities: ### Models with Native /v1/responses Support These models natively support the Responses API format on upstream providers: | Model Series | Examples | |-------------|----------| | **o1 Series** | `o1-pro` | | **o3 Series** | `o3-mini-high`, `o3-deep-research` | | **o4 Series** | `o4-mini-high`, `o4-mini-deep-research` | | **GPT-5 Series** | `gpt-5.1`, `gpt-5.2`, `gpt-5.5`, `gpt-5-*` | | **Codex Models** | `gpt-5-codex`, `gpt-5-codex-*` | ### Responses-Only Models Some advanced models **only** support the `/v1/responses` endpoint and cannot be used with `/v1/chat/completions` or `/v1/messages`: | Model | Description | |-------|-------------| | `gpt-5-pro` | GPT-5 Pro variant | | `gpt-5-codex-*` | GPT-5 Codex variants | | `o1-pro` | O1 Pro | | `codex-mini` | Codex Mini | > **Warning: Responses-Only Models** > > When using responses-only models, you must use the `/v1/responses` endpoint. Requests to `/v1/chat/completions` or `/v1/messages` will return an error for these models. ### All Other Models For models that don't natively support `/v1/responses` (like Claude, Gemini, GPT-4o, etc.), Apertis automatically: 1. Routes the request via `/v1/chat/completions` internally 2. Converts the response back to Responses API format 3. Returns the response in the expected format This means you can use **any model** with the Responses API - the conversion is handled transparently. | Provider | Example Models | Native Support | |----------|---------------|----------------| | Anthropic | `claude-sonnet-4-6`, `claude-opus-4-8` | Via fallback | | Google | `gemini-3.1-pro-preview`, `gemini-3-flash-preview` | Via fallback | | Meta | `llama-3.1-70b-instruct`, `llama-3.1-8b-instruct` | Via fallback | | xAI | `grok-4.3`, `grok-4.5` | Via fallback | ## Differences from Chat Completions | Feature | Responses API | Chat Completions | |---------|--------------|------------------| | Input field | `input` | `messages` | | String input | Supported | Not supported | | Instructions | `instructions` parameter | System message in `messages` | | Reasoning config | `reasoning` object | `reasoning_effort` string | | Text config | `text` object | Not available | | State management | `store`, `previous_response_id` | Manual message array | | Token limit | `max_output_tokens` | `max_tokens` | | Tool call limits | `max_tool_calls` | Not available | | Truncation | `truncation` parameter | Not available | ## Context Compression The Responses API supports [context compression](/api/text-generation/context-compression) to automatically reduce token usage for long conversations. Enable it via the request body or HTTP headers: ```bash tab="cURL" curl https://api.apertis.ai/v1/responses \ -H "Content-Type: application/json" \ -H "Authorization: Bearer " \ -d '{ "model": "gpt-5.2", "input": [ {"role": "user", "content": "Explain distributed systems"}, {"role": "assistant", "content": "Distributed systems are..."}, {"role": "user", "content": "Summarize the key points"} ], "compression": {"enabled": true, "strategy": "on", "model": "gemini-3-flash-preview"} }' ``` ```python tab="Python" response = client.responses.create( model="gpt-5.2", input=[ {"role": "user", "content": "Explain distributed systems"}, {"role": "assistant", "content": "Distributed systems are..."}, # ... long conversation history ... {"role": "user", "content": "Summarize the key points"} ], extra_body={ "compression": { "enabled": True, "strategy": "on", "model": "gemini-3-flash-preview" } } ) ``` ```javascript tab="JavaScript" const response = await client.responses.create({ model: 'gpt-5.2', input: [ { role: 'user', content: 'Explain distributed systems' }, { role: 'assistant', content: 'Distributed systems are...' }, // ... long conversation history ... { role: 'user', content: 'Summarize the key points' } ], compression: { enabled: true, strategy: 'on', model: 'gemini-3-flash-preview' } }); ``` See [Context Compression](/api/text-generation/context-compression) for full configuration options and strategies. ## Related Topics - [Chat Completions](/api/text-generation/chat-completions) - Traditional chat completion format - [Messages API](/api/text-generation/messages) - Anthropic-compatible format - [Context Compression](/api/text-generation/context-compression) - Reduce token usage for long conversations - [Models](/api/utilities/models) - List available models --- Source: https://docs.apertis.ai/api/text-generation/messages # Messages API (Native Anthropic) ``` POST /v1/messages ``` The Messages API is a **native Anthropic endpoint** that routes directly to Anthropic-type channels. This endpoint uses the native Anthropic request/response format without any conversion, allowing you to use Anthropic SDKs directly with Apertis. > **Info: Native Endpoint** > > This endpoint exclusively routes to Anthropic-type channels, ensuring full compatibility with Anthropic's API format including streaming, tool use, and all Claude-specific features. ## HTTP Request ```bash tab="cURL" curl https://api.apertis.ai/v1/messages \ -H "Content-Type: application/json" \ -H "x-api-key: " \ -d '{ "model": "claude-opus-4-5-20251101", "max_tokens": 1024, "messages": [ {"role": "user", "content": "Hello, Claude!"} ] }' ``` ```python tab="Python" import anthropic client = anthropic.Anthropic( api_key="", base_url="https://api.apertis.ai" ) message = client.messages.create( model="claude-opus-4-5-20251101", max_tokens=1024, messages=[ {"role": "user", "content": "Hello, Claude!"} ] ) print(message.content[0].text) ``` ```typescript tab="TypeScript" import Anthropic from '@anthropic-ai/sdk'; const client = new Anthropic({ apiKey: '', baseURL: 'https://api.apertis.ai' }); const message: Anthropic.Message = await client.messages.create({ model: 'claude-opus-4-5-20251101', max_tokens: 1024, messages: [ { role: 'user', content: 'Hello, Claude!' } ] }); const block = message.content[0]; if (block.type === 'text') console.log(block.text); ``` > **Note** > > The `anthropic-version` header is optional and will be ignored. Apertis handles API version compatibility automatically. ## Authentication The Messages API supports both authentication methods: | Header | Format | Example | |--------|--------|---------| | `x-api-key` | Anthropic style | `x-api-key: sk-your-api-key` | | `Authorization` | Bearer token | `Authorization: Bearer sk-your-api-key` | ## Parameters ### Required Parameters | Parameter | Type | Description | |-----------|------|-------------| | `model` | string | The Claude model to use | | `messages` | array | Array of message objects | | `max_tokens` | integer | Maximum tokens in the response | ### Optional Parameters (Native Anthropic) | Parameter | Type | Description | |-----------|------|-------------| | `system` | string | System prompt (top-level, not in messages) | | `temperature` | number | Sampling temperature (0-1). Default: 1 | | `top_p` | number | Nucleus sampling threshold (0-1) | | `top_k` | integer | Top-k sampling (Anthropic specific) | | `stream` | boolean | Enable streaming. Default: false | | `stop_sequences` | array | Custom stop sequences | | `tools` | array | Tools/functions the model can call | | `tool_choice` | object | Controls tool selection behavior | | `metadata` | object | Request metadata (e.g., user_id) | | `thinking` | object | Extended thinking configuration (see below) | ### Thinking Parameter The `thinking` parameter enables Claude's extended thinking capability for more complex reasoning: | Option | Type | Description | |--------|------|-------------| | `type` | string | `"enabled"` or `"disabled"` | | `budget_tokens` | integer | Token budget for thinking (1024-32768) | ```bash tab="cURL" curl https://api.apertis.ai/v1/messages \ -H "Content-Type: application/json" \ -H "x-api-key: " \ -d '{ "model": "claude-opus-4-5-20251101", "max_tokens": 4096, "thinking": {"type": "enabled", "budget_tokens": 10240}, "messages": [ {"role": "user", "content": "Solve this complex math problem step by step..."} ] }' ``` ```python tab="Python" # Extended Thinking Example message = client.messages.create( model="claude-opus-4-5-20251101", max_tokens=4096, thinking={ "type": "enabled", "budget_tokens": 10240 }, messages=[ {"role": "user", "content": "Solve this complex math problem step by step..."} ] ) ``` ```typescript tab="TypeScript" // Extended Thinking Example const message = await client.messages.create({ model: 'claude-opus-4-5-20251101', max_tokens: 4096, thinking: { type: 'enabled', budget_tokens: 10240 }, messages: [ { role: 'user', content: 'Solve this complex math problem step by step...' } ] }); ``` ### Extended Parameters (OpenAI-compatible) These additional parameters are supported for compatibility with upstream providers: | Parameter | Type | Description | |-----------|------|-------------| | `n` | integer | Number of completions to generate. Default: 1 | | `stop` | string/array | Up to 4 sequences where the API will stop generating | | `presence_penalty` | number | Penalize new topics (-2.0 to 2.0). Default: 0 | | `frequency_penalty` | number | Penalize repetition (-2.0 to 2.0). Default: 0 | | `logit_bias` | object | Map of token IDs to bias values (-100 to 100) | | `user` | string | Unique identifier for end-user tracking | | `response_format` | object | Specify output format (e.g., JSON mode) | | `seed` | number | Seed for deterministic sampling | ## Example Usage ### Python (Anthropic SDK) ```python import anthropic client = anthropic.Anthropic( api_key="sk-your-api-key", base_url="https://api.apertis.ai" ) message = client.messages.create( model="claude-sonnet-4-6", max_tokens=1024, messages=[ {"role": "user", "content": "What is the meaning of life?"} ] ) print(message.content[0].text) ``` ### JavaScript (Anthropic SDK) ```javascript import Anthropic from '@anthropic-ai/sdk'; const client = new Anthropic({ apiKey: 'sk-your-api-key', baseURL: 'https://api.apertis.ai' }); const message = await client.messages.create({ model: 'claude-sonnet-4-6', max_tokens: 1024, messages: [ { role: 'user', content: 'What is the meaning of life?' } ] }); console.log(message.content[0].text); ``` ### With System Prompt ```bash tab="cURL" curl https://api.apertis.ai/v1/messages \ -H "Content-Type: application/json" \ -H "x-api-key: " \ -d '{ "model": "claude-opus-4-5-20251101", "max_tokens": 1024, "system": "You are a helpful assistant that speaks like a pirate.", "messages": [ {"role": "user", "content": "Tell me about the weather."} ] }' ``` ```python tab="Python" message = client.messages.create( model="claude-opus-4-5-20251101", max_tokens=1024, system="You are a helpful assistant that speaks like a pirate.", messages=[ {"role": "user", "content": "Tell me about the weather."} ] ) ``` ```typescript tab="TypeScript" const message = await client.messages.create({ model: 'claude-opus-4-5-20251101', max_tokens: 1024, system: 'You are a helpful assistant that speaks like a pirate.', messages: [ { role: 'user', content: 'Tell me about the weather.' } ] }); ``` ### Multi-turn Conversation ```bash tab="cURL" curl https://api.apertis.ai/v1/messages \ -H "Content-Type: application/json" \ -H "x-api-key: " \ -d '{ "model": "claude-opus-4-5-20251101", "max_tokens": 1024, "messages": [ {"role": "user", "content": "What is Python?"}, {"role": "assistant", "content": "Python is a high-level programming language..."}, {"role": "user", "content": "How do I install it?"} ] }' ``` ```python tab="Python" message = client.messages.create( model="claude-opus-4-5-20251101", max_tokens=1024, messages=[ {"role": "user", "content": "What is Python?"}, {"role": "assistant", "content": "Python is a high-level programming language..."}, {"role": "user", "content": "How do I install it?"} ] ) ``` ```typescript tab="TypeScript" const message = await client.messages.create({ model: 'claude-opus-4-5-20251101', max_tokens: 1024, messages: [ { role: 'user', content: 'What is Python?' }, { role: 'assistant', content: 'Python is a high-level programming language...' }, { role: 'user', content: 'How do I install it?' } ] }); ``` ### Streaming ```python with client.messages.stream( model="claude-sonnet-4-6", max_tokens=1024, messages=[{"role": "user", "content": "Write a poem about coding."}] ) as stream: for text in stream.text_stream: print(text, end="", flush=True) ``` ### Streaming with curl ```bash curl https://api.apertis.ai/v1/messages \ -H "x-api-key: " \ -H "Content-Type: application/json" \ -H "anthropic-version: 2023-06-01" \ -d '{ "model": "claude-opus-4-5-20251101", "max_tokens": 100, "stream": true, "messages": [{"role": "user", "content": "Hello!"}] }' ``` SSE events returned: - `message_start` - Initial message metadata - `content_block_start` - Start of content block - `content_block_delta` - Incremental text chunks - `content_block_stop` - End of content block - `message_delta` - Final usage and stop reason - `message_stop` - Stream complete ### Vision (Image Input) ```bash tab="cURL" curl https://api.apertis.ai/v1/messages \ -H "Content-Type: application/json" \ -H "x-api-key: " \ -d '{ "model": "claude-opus-4-5-20251101", "max_tokens": 1024, "messages": [ { "role": "user", "content": [ { "type": "image", "source": {"type": "base64", "media_type": "image/png", "data": ""} }, {"type": "text", "text": "What do you see in this image?"} ] } ] }' ``` ```python tab="Python" import base64 with open("image.png", "rb") as f: image_data = base64.standard_b64encode(f.read()).decode("utf-8") message = client.messages.create( model="claude-opus-4-5-20251101", max_tokens=1024, messages=[ { "role": "user", "content": [ { "type": "image", "source": { "type": "base64", "media_type": "image/png", "data": image_data } }, { "type": "text", "text": "What do you see in this image?" } ] } ] ) ``` ```typescript tab="TypeScript" import fs from 'node:fs'; const imageData = fs.readFileSync('image.png').toString('base64'); const message = await client.messages.create({ model: 'claude-opus-4-5-20251101', max_tokens: 1024, messages: [ { role: 'user', content: [ { type: 'image', source: { type: 'base64', media_type: 'image/png', data: imageData } }, { type: 'text', text: 'What do you see in this image?' } ] } ] }); ``` ### PDF Document Input Claude can analyze PDF documents directly. Use the `document` content type with base64-encoded PDF data: ```bash tab="cURL" curl https://api.apertis.ai/v1/messages \ -H "Content-Type: application/json" \ -H "x-api-key: " \ -d '{ "model": "claude-opus-4-5-20251101", "max_tokens": 4096, "messages": [ { "role": "user", "content": [ { "type": "document", "source": {"type": "base64", "media_type": "application/pdf", "data": ""} }, {"type": "text", "text": "Summarize this document."} ] } ] }' ``` ```python tab="Python" import base64 with open("document.pdf", "rb") as f: pdf_data = base64.standard_b64encode(f.read()).decode("utf-8") message = client.messages.create( model="claude-opus-4-5-20251101", max_tokens=4096, messages=[ { "role": "user", "content": [ { "type": "document", "source": { "type": "base64", "media_type": "application/pdf", "data": pdf_data } }, { "type": "text", "text": "Summarize this document." } ] } ] ) ``` ```typescript tab="TypeScript" import fs from 'node:fs'; const pdfData = fs.readFileSync('document.pdf').toString('base64'); const message = await client.messages.create({ model: 'claude-opus-4-5-20251101', max_tokens: 4096, messages: [ { role: 'user', content: [ { type: 'document', source: { type: 'base64', media_type: 'application/pdf', data: pdfData } }, { type: 'text', text: 'Summarize this document.' } ] } ] }); ``` #### Document Source Options | Field | Type | Description | |-------|------|-------------| | `type` | string | `"base64"` for encoded data | | `media_type` | string | `"application/pdf"` for PDF files | | `data` | string | Base64-encoded document content | > **Tip: Supported Formats** > > Currently, PDF (`application/pdf`) is the primary supported document format for Claude models. ## Response Format ```json { "id": "msg_abc123", "type": "message", "role": "assistant", "content": [ { "type": "text", "text": "Hello! How can I help you today?" } ], "model": "claude-sonnet-4-6", "stop_reason": "end_turn", "usage": { "input_tokens": 12, "output_tokens": 10 } } ``` ## Supported Models The Messages API supports Claude models via Anthropic-type channels: | Model | Description | |-------|-------------| | `claude-opus-4-5-20251101` | Claude Opus 4.5 - most capable | | `claude-sonnet-4-6` | Claude Sonnet 4.6 - balanced | | `claude-haiku-4.5` | Claude Haiku 4.5 - fast and efficient | > **Warning: Anthropic Channels Only** > > This endpoint routes exclusively to Anthropic-type channels. If you need to access non-Claude models, use the [Chat Completions](/api/text-generation/chat-completions) endpoint instead. > > Note: Some advanced OpenAI models (like `gpt-5-pro`, `o1-pro`, `gpt-5-codex-*`) only support the [Responses API](/api/text-generation/responses) and cannot be used with this endpoint. ## Differences from Direct Anthropic API | Feature | Apertis | Direct Anthropic | |---------|---------|------------------| | Base URL | `https://api.apertis.ai` | `https://api.anthropic.com` | | API Key | Apertis API key | Anthropic API key | | Request Format | Native Anthropic (no conversion) | Native Anthropic | | Streaming | Full SSE support | Full SSE support | | Billing | Unified Apertis billing | Anthropic billing | ## Context Compression The Messages API supports [context compression](/api/text-generation/context-compression) to automatically summarize older conversation history and reduce token usage. ### Using the Anthropic SDK ```python message = client.messages.create( model="claude-sonnet-4-6", max_tokens=1024, messages=[ {"role": "user", "content": "Hello!"} ], extra_body={ "compression": { "enabled": True, "strategy": "on", "model": "gpt-5.4-mini" } } ) ``` ### Using cURL ```bash curl https://api.apertis.ai/v1/messages \ -H "x-api-key: " \ -H "Content-Type: application/json" \ -d '{ "model": "claude-sonnet-4-6", "max_tokens": 1024, "messages": [{"role": "user", "content": "Hello!"}], "compression": {"enabled": true, "model": "gpt-5.4-mini"} }' ``` See [Context Compression](/api/text-generation/context-compression) for full documentation including strategies, configuration, and response headers. ## Related Topics - [Chat Completions](/api/text-generation/chat-completions) - OpenAI-compatible format - [Responses API](/api/text-generation/responses) - OpenAI Responses format - [Context Compression](/api/text-generation/context-compression) - Reduce token usage for long conversations - [Models](/api/utilities/models) - List available models --- Source: https://docs.apertis.ai/api/text-generation/streaming # Streaming Output ## Python Example ```python from openai import OpenAI client = OpenAI( base_url="https://api.apertis.ai/v1", api_key="" ) stream = client.chat.completions.create( model="", messages=[{"role": "user", "content": "Say this is a test"}], stream=True, ) for chunk in stream: if chunk.choices[0].delta.content is not None: print(chunk.choices[0].delta.content, end="") ``` ## Parameters - ``: The model to use, see [Model List](https://apertis.ai/models). - `stream`: Whether to enable streaming output, currently only supports `True`. - `APERTIS_API_KEY`: Your API key. --- Source: https://docs.apertis.ai/api/text-generation/structured-output # Structured Output Through Apertis's structured output feature, developers can convert API responses into specific JSON formats, which is particularly useful for application scenarios requiring specific data structures. ## Use Example ``` import http.client import json conn = http.client.HTTPSConnection("api.apertis.ai") payload = json.dumps({ "model": "gpt-5.5", "messages": [ { "role": "system", "content": "You are a helpful math tutor. Guide the user through the solution step by step." }, { "role": "user", "content": "how can I solve 8x + 7 = -23" } ], "response_format": { "type": "json_schema", "json_schema": { "name": "math_reasoning", "schema": { "type": "object", "properties": { "steps": { "type": "array", "items": { "type": "object", "properties": { "explanation": { "type": "string" }, "output": { "type": "string" } }, "required": [ "explanation", "output" ], "additionalProperties": False } }, "final_answer": { "type": "string" } }, "required": [ "steps", "final_answer" ], "additionalProperties": False }, "strict": True } } }) headers = { 'Accept': 'application/json', 'Authorization': 'Bearer ', 'Content-Type': 'application/json' } conn.request("POST", "/v1/chat/completions", payload, headers) res = conn.getresponse() data = res.read() print(data.decode("utf-8")) ``` ## Parameter - `model`: See [Model List](https://apertis.ai/models). - `APERTIS_API_KEY`: Your Apertis [API Key](https://apertis.ai/setting?tab=keys). --- Source: https://docs.apertis.ai/api/text-generation/prompt-cache # Prompt Cache ## Overview Apertis includes a powerful Prompt Cache feature that significantly improves system performance, reduces processing time for repeated requests, and lowers API costs. When the same prompt is requested multiple times, the system returns results directly from the cache without calling the upstream AI model again. > **Tip: Free Feature** > > Prompt Cache is completely **free** for all Apertis users. Cache hits do not consume any credits or quota from your account. ## Key Features ### Core Benefits - **Performance Boost**: Cache hits reduce response time from seconds to milliseconds - **Zero Cost**: Cache hits are completely free — no credits or quota consumed - **Exact Matching**: Supports exact-match cache responses - **Automatic Management**: Built-in TTL (Time To Live) mechanism automatically cleans expired cache ### Technical Architecture - **Cache Strategy**: LRU (Least Recently Used) eviction policy - **Concurrency Safe**: Supports cache operations in high-concurrency environments - **Fault Tolerance**: Automatically falls back to normal request flow when cache fails ## Cache Strategy ### Cache Key Generation The system generates unique cache keys using: - Model name - Prompt content - System message - Temperature parameter - Other relevant parameters ### Cache Hit Conditions A cache hit requires: 1. Exact same prompt content 2. Same model and parameter settings 3. Cache item not expired 4. Cache size within limits ### Cache Invalidation Cache invalidates when: - Exceeds TTL setting - Redis storage space insufficient - Manual cache clearing - System restart (if not persisted) --- Source: https://docs.apertis.ai/api/text-generation/context-compression # Context Compression ``` POST /v1/chat/completions POST /v1/messages POST /v1/responses ``` Context compression automatically summarizes older conversation history to reduce token usage and cost, while preserving recent context. This is especially useful for long-running conversations where token counts grow significantly. ## How It Works When enabled, Apertis analyzes the conversation before forwarding it to the target model: 1. **Token threshold check** — Compression only triggers when the conversation exceeds a configurable token threshold 2. **Message segmentation** — Messages are split into system prompts, compressible history, and recent turns (which are always preserved) 3. **Cost-effectiveness check** — Compression is skipped if the cost of running the compression model exceeds the estimated savings 4. **Summarization** — Older messages are summarized by a lightweight model (e.g., `gpt-4.1-mini`, `claude-haiku-4.5`, or `gemini-3-flash-preview`) 5. **Injection** — The summary replaces the older messages, and recent turns are preserved verbatim > **Info: Transparent to Your Application** > > Compression happens at the gateway level. Your application sends the full conversation as usual — Apertis handles compression automatically and returns the response as normal. The only visible difference is reduced token usage. ## Enabling Compression There are three ways to enable context compression, with the following priority order: **Request body** > **Request headers** > **Token-level defaults** ### Method 1: Request Body (Recommended for SDKs) Add a `compression` object to your request body. This works with the OpenAI SDK via `extra_body` and with the Anthropic SDK via `extra_body`. #### OpenAI SDK (Python) ```python from openai import OpenAI client = OpenAI( api_key="YOUR_API_KEY", base_url="https://api.apertis.ai/v1" ) response = client.chat.completions.create( model="gpt-5.4-mini", messages=[ {"role": "user", "content": "Hello!"} ], extra_body={ "compression": { "enabled": True, "strategy": "on", # "on", "conservative", or "aggressive" "threshold": 8000, # Token threshold to trigger compression "keep_turns": 6, # Recent turns to always preserve "model": "auto" # Compression model ("auto" or specific model) } } ) print(response.choices[0].message.content) ``` #### Anthropic SDK (Python) ```python import anthropic client = anthropic.Anthropic( api_key="YOUR_API_KEY", base_url="https://api.apertis.ai" ) message = client.messages.create( model="claude-sonnet-4-6", max_tokens=1024, messages=[ {"role": "user", "content": "Hello!"} ], extra_body={ "compression": { "enabled": True, "strategy": "on", "model": "gpt-5.4-mini" } } ) print(message.content[0].text) ``` #### cURL ```bash curl https://api.apertis.ai/v1/chat/completions \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-5.4-mini", "messages": [ {"role": "user", "content": "Hello!"} ], "compression": { "enabled": true, "strategy": "on", "model": "gpt-5.4-mini" } }' ``` #### OpenAI SDK — Responses API (Python) Compression also works with the `/v1/responses` endpoint. The `input` field (string or array of messages) is automatically converted for compression. ```python from openai import OpenAI client = OpenAI( api_key="YOUR_API_KEY", base_url="https://api.apertis.ai/v1" ) response = client.responses.create( model="o4-mini-high", input=[ {"role": "user", "content": "Explain distributed systems"}, {"role": "assistant", "content": "Distributed systems are..."}, # ... long conversation history ... {"role": "user", "content": "Summarize the key points"} ], extra_body={ "compression": { "enabled": True, "strategy": "aggressive", "model": "gpt-5.4-mini" } } ) print(response.output[0].content[0].text) ``` #### cURL — Responses API ```bash curl https://api.apertis.ai/v1/responses \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "o4-mini-high", "input": [ {"role": "user", "content": "Hello!"} ], "compression": { "enabled": true, "strategy": "on", "model": "gpt-5.4-mini" } }' ``` #### Node.js / TypeScript ```typescript const response = await fetch('https://api.apertis.ai/v1/chat/completions', { method: 'POST', headers: { 'Authorization': 'Bearer YOUR_API_KEY', 'Content-Type': 'application/json' }, body: JSON.stringify({ model: 'gpt-5.4-mini', messages: [ { role: 'user', content: 'Hello!' } ], compression: { enabled: true, strategy: 'on', model: 'gpt-5.4-mini' } }) }); const data = await response.json(); console.log(data.choices[0].message.content); ``` ### Method 2: Request Headers Add compression headers to individual requests. Useful for cURL or custom HTTP clients. Works with all supported endpoints. ```bash curl https://api.apertis.ai/v1/chat/completions \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -H "X-Context-Compression: on" \ -H "X-Compression-Threshold: 8000" \ -H "X-Compression-Keep-Turns: 6" \ -H "X-Compression-Model: auto" \ -d '{ "model": "gpt-5.4-mini", "messages": [{"role": "user", "content": "Hello!"}] }' ``` The same headers work with `/v1/responses`: ```bash curl https://api.apertis.ai/v1/responses \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -H "X-Context-Compression: aggressive" \ -H "X-Compression-Model: gpt-5.4-mini" \ -d '{ "model": "o4-mini-high", "input": [{"role": "user", "content": "Hello!"}] }' ``` | Header | Values | Description | |--------|--------|-------------| | `X-Context-Compression` | `on`, `conservative`, `aggressive`, `off` | Compression strategy | | `X-Compression-Threshold` | integer (e.g., `8000`) | Token count threshold to trigger compression | | `X-Compression-Keep-Turns` | integer (e.g., `6`) | Number of recent turns to always preserve | | `X-Compression-Model` | model ID or `auto` | Model used for summarization | ### Method 3: Token-Level Defaults Configure compression defaults on your API key via the Apertis dashboard. Go to **API Keys** → **Edit** → **Compression** tab. This sets default compression behavior for all requests made with that key, without any code changes. Per-request settings (body or headers) override token-level defaults. ## Configuration Parameters ### `compression` Object | Parameter | Type | Default | Description | |-----------|------|---------|-------------| | `enabled` | boolean | `false` | Enable or disable compression for this request | | `strategy` | string | `"on"` | Compression strategy — controls how aggressively older messages are summarized (see **Strategies** below) | | `threshold` | integer | `8000` | Minimum total token count before compression activates. Conversations shorter than this threshold are sent to the model as-is, without any compression | | `keep_turns` | integer | Per strategy | Number of most recent conversation turns to keep uncompressed. Older turns beyond this count are summarized into a condensed context. Set to `0` to use the strategy default | | `model` | string | `"auto"` | Model used to generate conversation summaries. Use `"auto"` to let Apertis select a cost-efficient model, or specify a model ID from your available models | ### Strategies | Strategy | Keep Turns | Description | |----------|-----------|-------------| | `on` | 6 | **Balanced** — good default for most use cases. Keeps 6 recent turns uncompressed, summarizes older history | | `conservative` | 8 | **Preserves more context** — keeps 8 recent turns, compresses less aggressively. Best when recent conversation details matter | | `aggressive` | 3 | **Maximum token savings** — keeps only 3 recent turns, summarizes everything else. Best for very long conversations where cost reduction is the priority | ### Auto Model Selection When `model` is set to `"auto"` (default), Apertis automatically selects a cost-efficient compression model based on the target model you are calling: - **Claude models** → `claude-haiku-4.5` (low-cost Claude targets → `gpt-4.1-mini`) - **Gemini models** → `gemini-3-flash-preview` (Gemini flash targets → `gpt-4.1-mini`) - **All other models** (OpenAI, etc.) → `gpt-4.1-mini` (`gpt-4.1-mini` and `gpt-4o-mini` targets → `claude-haiku-4.5`) The auto-selection avoids using the same model for both compression and the target request (e.g., if you call `claude-haiku-4.5`, compression falls back to `gpt-4.1-mini` instead). You can also specify any available model explicitly (e.g., `"gpt-5.4-mini"`, `"claude-haiku-4.5"`, `"gemini-3-flash-preview"`). You can browse available models on the dashboard's Model Detail page or select from the dropdown in the API Key Compression tab. ## Response Headers When compression is enabled, Apertis adds response headers to indicate compression status: ### When Compression is Applied | Header | Example | Description | |--------|---------|-------------| | `X-Compression-Applied` | `true` | Compression was applied | | `X-Compression-Original-Tokens` | `50000` | Original token count before compression | | `X-Compression-Final-Tokens` | `8000` | Token count after compression | | `X-Compression-Savings` | `84%` | Percentage of tokens saved | ### When Compression is Skipped | Header | Example | Description | |--------|---------|-------------| | `X-Compression-Applied` | `false` | Compression was not applied | | `X-Compression-Error` | `compression-not-cost-effective` | Machine-readable reason | | `X-Compression-Message` | *(human-readable explanation)* | Detailed explanation | #### Error Codes | Error Code | Meaning | |------------|---------| | `compression-not-cost-effective` | The cost of running the compression model exceeds the estimated token savings. Common with short conversations. | | `compression-model-unavailable:` | The specified compression model is not available. Use `"auto"` or check the model name. | | `compression-call-failed` | The compression model call failed. The original request proceeds without compression. | ## Multimodal Safety Compression automatically protects multimodal content: - Messages containing **images**, **audio**, or **documents** are moved to the "recent" segment and are never compressed - Only text-based messages in the compressible history are summarized ## Cost Considerations Compression has its own cost (the summarization call), so Apertis performs an automatic **cost-effectiveness check** before compressing: - If the estimated savings from compression outweigh the cost of the summarization call, compression proceeds - If compression would cost more than it saves (common with short conversations), it is skipped automatically - The `X-Compression-Error: compression-not-cost-effective` header indicates when this happens > **Tip: When to Enable Compression** > > Context compression is most effective for: > - **Long-running conversations** (20+ turns) > - **Expensive target models** (e.g., GPT-4, Claude Opus) where token savings are significant > - **Chatbot applications** where conversations accumulate over time > > It is less effective for: > - Short conversations (< 10 turns) > - Cheap models where the compression cost may exceed savings > - Single-turn requests ## Supported Endpoints | Endpoint | Supported | |----------|-----------| | `/v1/chat/completions` | Yes | | `/v1/messages` | Yes | | `/v1/responses` | Yes | | `/v1/images/generations` | No | | `/v1/audio/*` | No | | `/v1/embeddings` | No | ## Graceful Degradation Compression never blocks your main request. If compression fails for any reason: - The original uncompressed conversation is forwarded to the target model - The response is returned normally (HTTP 200) - Compression status is communicated via response headers only Your application does not need special error handling for compression failures. --- Source: https://docs.apertis.ai/api/search/web-search # Web Search Enable real-time web search for any model by adding the `:web` suffix to the model name. The system automatically searches for relevant information and integrates it into the response. > **Warning: Note** > > Web search is only available for non-free models. Free models (e.g., `gemma-4-31b-it:free`) do not support the `:web` suffix and will return an error. ## Quick Start Simply append `:web` to any model ID: ```bash curl https://api.apertis.ai/v1/chat/completions \ -H "Authorization: Bearer $APERTIS_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-5.4-mini:web", "messages": [ {"role": "user", "content": "What is the stock price of Apple today?"} ], "max_tokens": 100 }' ``` ## Supported Models All non-free models support the `:web` suffix: | Provider | Example Models | |----------|----------------| | OpenAI | `gpt-5.5:web`, `gpt-5.4-mini:web`, `o4-mini-high:web` | | Anthropic | `claude-sonnet-4-6:web`, `claude-opus-4-5-20251101:web` | | Google | `gemini-3.1-pro-preview:web`, `gemini-3-flash-preview:web` | | Others | Any available non-free model can use `:web` | ## Request Parameters In addition to standard Chat Completions parameters, you can use these web search specific parameters: | Parameter | Type | Default | Description | |-----------|------|---------|-------------| | `web_results_count` | integer | 5 | Number of search results to retrieve (1-10) | | `web_content_length` | string | "medium" | Content length: `short`, `medium`, or `full` | ### Example Request ```json { "model": "gpt-5.4-mini:web", "messages": [ {"role": "user", "content": "What is the stock price of Apple today?"} ], "max_tokens": 100, "web_results_count": 5, "web_content_length": "medium" } ``` ## Response Format The response includes a `web_sources` array listing all referenced sources: ```json { "model": "gpt-5.4-mini", "system_fingerprint": "fp_f97eff32c5", "choices": [ { "index": 0, "finish_reason": "stop", "message": { "role": "assistant", "content": "As of January 6, 2026, the stock price of Apple (NASDAQ: AAPL) is $262.36, reflecting a decline of $4.90 from the previous day [1]." } } ], "usage": { "prompt_tokens": 1265, "completion_tokens": 43, "total_tokens": 1308 }, "web_sources": [ { "title": "Stock Price - Apple Investor Relations", "url": "https://investor.apple.com/stock-price/default.aspx", "snippet": "Stock Price - Apple\nJanuary 6, 2026 4:00 p.m. ET..." }, { "title": "NASDAQ:AAPL Stock Price — TradingView", "url": "https://www.tradingview.com/symbols/NASDAQ-AAPL/", "snippet": "Apple Stock Chart — NASDAQ:AAPL Stock Price..." }, { "title": "Apple Stock Price Today | NASDAQ: AAPL Live - Investing.com", "url": "https://www.investing.com/equities/apple-computer-inc", "snippet": "Apple Stock Price Today | NASDAQ: AAPL Live..." }, { "title": "AAPL Stock Price | Apple Inc. Stock Quote | MarketWatch", "url": "https://www.marketwatch.com/investing/stock/aapl", "snippet": "AAPL Stock Price | Apple Inc. Stock Quote..." } ], "error": { "message": "", "type": "", "param": "", "code": null } } ``` ## Streaming Mode When using streaming mode with `"stream": true`, a search indicator appears first, followed by the model response chunks: ### Streaming Request ```json { "model": "gpt-5.4-mini:web", "messages": [ {"role": "user", "content": "What is the weather in Tokyo today?"} ], "max_tokens": 80, "stream": true } ``` ### Streaming Response ``` data: {"id":"web-search","object":"chat.completion.chunk","created":1768287477,"model":"","choices":[{"index":0,"delta":{"role":"assistant","content":"🔍 Web searching...\n\n"}}]} data: {"id":"chatcmpl-xxx","object":"chat.completion.chunk","created":1768287475,"model":"gpt-5.4-mini","choices":[{"index":0,"delta":{"role":"assistant","content":""}}]} data: {"id":"chatcmpl-xxx","object":"chat.completion.chunk","created":1768287475,"model":"gpt-5.4-mini","choices":[{"index":0,"delta":{"content":"The"}}]} data: {"id":"chatcmpl-xxx","object":"chat.completion.chunk","created":1768287475,"model":"gpt-5.4-mini","choices":[{"index":0,"delta":{"content":" weather"}}]} data: {"id":"chatcmpl-xxx","object":"chat.completion.chunk","created":1768287475,"model":"gpt-5.4-mini","choices":[{"index":0,"delta":{"content":" in"}}]} data: {"id":"chatcmpl-xxx","object":"chat.completion.chunk","created":1768287475,"model":"gpt-5.4-mini","choices":[{"index":0,"delta":{"content":" Tokyo"}}]} ... data: [DONE] ``` The first chunk contains the `🔍 Web searching...` indicator with `id: "web-search"`, followed by regular streaming chunks from the model. ## HTTP Headers The response includes these HTTP headers for monitoring web search status: | Header | Description | |--------|-------------| | `X-Web-Search-Used` | `true` or `false`, indicates whether web search was used | | `X-Web-Search-Count` | Number of search results retrieved | | `X-Web-Search-Status` | Search status: `success`, `failed`, or `no_results` | Example response headers: ``` x-web-search-used: true x-web-search-count: 5 x-web-search-status: success ``` ## Use Cases ### 1. Real-Time Information Query current news, stock prices, weather, and other real-time information: ```bash curl https://api.apertis.ai/v1/chat/completions \ -H "Authorization: Bearer $APERTIS_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-5.4-mini:web", "messages": [ {"role": "user", "content": "What is the latest news about AI?"} ], "max_tokens": 150 }' ``` ### 2. Technical Documentation Query the latest technical documentation or API updates: ```json { "model": "claude-sonnet-4-6:web", "messages": [ {"role": "user", "content": "How do I use Server Actions in Next.js 15?"} ], "web_results_count": 8, "web_content_length": "full" } ``` ### 3. Academic Research Search for the latest research papers or academic information: ```json { "model": "gemini-3.1-pro-preview:web", "messages": [ {"role": "user", "content": "What are the important findings in 2024 research on large language models?"} ] } ``` ## Billing - **Search costs**: Absorbed by the platform, no additional charge to users - **Model costs**: Only the final response token usage is counted - **Query generation**: Internal query generation is not counted against user quota > **Tip: Token Usage** > > When web search is enabled, the `prompt_tokens` in the response will be higher than a regular request because the search results are injected into the system message. In the example above, the prompt tokens increased from ~13 (without search) to ~1265 (with search results injected). ## Error Handling When web search fails, the system automatically falls back to non-search mode and continues using the base model to respond. You can check the search status via the `X-Web-Search-Status` header: - `success`: Web search completed successfully - `failed`: Web search failed, fell back to non-search mode - `no_results`: Web search returned no results, fell back to non-search mode ## Best Practices 1. **Clear questions**: The more specific the question, the more accurate the search results 2. **Appropriate result count**: For general queries, 3-5 results are usually sufficient 3. **Choose appropriate content length**: - `short`: Quick summaries - `medium`: Balance between detail and speed (default) - `full`: Use when complete context is needed 4. **Check sources**: Use `web_sources` in the response to verify information sources 5. **Monitor headers**: Check `X-Web-Search-Status` to confirm search was successful --- Source: https://docs.apertis.ai/api/vision/read-image # Read Image with LLM ## Python Example ```python from openai import OpenAI client = OpenAI( base_url="https://api.apertis.ai/v1", api_key="" ) response = client.chat.completions.create( model="", messages=[ { "role": "user", "content": [ {"type": "text", "text": "What’s in this image?"}, { "type": "image_url", "image_url": { "url": "", }, }, ], } ], max_tokens=300, ) print(response.choices[0]) ``` ## Parameters - ``: The model to use, see [Model List](https://apertis.ai/models). - ``: The URL of the image to read. - `APERTIS_API_KEY`: Your API key. --- Source: https://docs.apertis.ai/api/vision/image-generation # Using Image Generation API Apertis provides Image Generation API for developers to generate images, primarily focusing on `GPT-4o Image Generation`, `Gemini 2.0 Flash Expanded Image Generation` and `Grok 3 Image Generation`. ## Usage (Python Example) ```python import requests import json url = "https://api.apertis.ai/v1/chat/completions" payload = json.dumps({ "max_tokens": 4096, "model": "gemini-2.5-flash-image", "messages": [ { "role": "user", "content": [ { "type": "text", "text": "Transform the image into Ghibli style" }, { "type": "image_url", "image_url": { "url": "data:image/png;base64," } } ] } ] }) headers = { 'Accept': 'application/json', 'Authorization': 'Bearer ', 'Content-Type': 'application/json' } response = requests.request("POST", url, headers=headers, data=payload) print(response.text) ``` ```python import requests import json url = "https://api.apertis.ai/v1/chat/completions" payload = json.dumps({ "max_tokens": 4096, "model": "gemini-2.5-flash-image", "messages": [ { "role": "user", "content": [ { "type": "text", "text": "Generate a beautiful landscape image" }, ] } ] }) headers = { 'Accept': 'application/json', 'Authorization': 'Bearer ', 'Content-Type': 'application/json' } response = requests.request("POST", url, headers=headers, data=payload) print(response.text) ``` ## Usage (bash Example) ```bash curl -X POST "https://api.apertis.ai/v1/chat/completions" \ -H "Accept: application/json" \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{"model": "gemini-2.5-flash-image", "messages": [{"role": "user", "content": [{"type": "text", "text": "Transform the image into Ghibli style"}, {"type": "image_url", "image_url": {"url": "data:image/png;base64,"}}]}]}' ``` ## Parameter Description - `model`: For example `gemini-2.5-flash-image` or `gemini-3.1-flash-lite-image`. See [Models](/models) for the current image models. - `prompt`: Description of the image to be generated. - `APERTIS_API_KEY`: Your API key. - Allowed Image Input: Convert to **base64** format. --- Source: https://docs.apertis.ai/api/vision/dalle # Using GPT Image Models ## Python Example ```python import requests import json url = "https://api.apertis.ai/v1/images/generations" payload = json.dumps({ "model": "gpt-image-2", "prompt": "A cute baby sea otter", "n": 1, "size": "1024x1024" }) api_key = "APERTIS_API_KEY" headers = { 'Authorization': f'Bearer {api_key}', 'Content-Type': 'application/json' } response = requests.request("POST", url, headers=headers, data=payload) print(response.text) ``` ## Parameters - `model`: The model to use, e.g. `gpt-image-2`. See [Models](/models) for the current image models - `size`: The size of the image: `1024x1024`, `1536x1024`, `1024x1536` or `auto` - `APERTIS_API_KEY`: Your API key --- Source: https://docs.apertis.ai/api/vision/images-api # Images API ``` POST /v1/images/generations ``` The Images API generates images from text prompts using GPT image models such as `gpt-image-2`. ## HTTP Request ```bash curl https://api.apertis.ai/v1/images/generations \ -H "Content-Type: application/json" \ -H "Authorization: Bearer " \ -d '{ "model": "gpt-image-2", "prompt": "A cute baby sea otter", "n": 1, "size": "1024x1024" }' ``` ## Authentication | Header | Format | Example | |--------|--------|---------| | `Authorization` | Bearer token | `Authorization: Bearer sk-your-api-key` | ## Parameters ### Required Parameters | Parameter | Type | Description | |-----------|------|-------------| | `prompt` | string | A text description of the desired image(s). See prompt limits below | ### Prompt Length Limits | Model | Max Prompt Length | |-------|-------------------| | `gpt-image-2` | 32000 characters | ### Optional Parameters | Parameter | Type | Description | |-----------|------|-------------| | `model` | string | The model to use for image generation, e.g. `gpt-image-2`. See [Models](/models) for the current image models | | `n` | integer | Number of images to generate (1-10). Default: 1 | | `size` | string | Size of the generated images (see Size Options below) | | `quality` | string | Quality of the image: `high`, `medium`, `low`, `auto`. Default: `auto` | | `response_format` | string | Format of the response: `url`, `b64_json`. Default: `url` | | `user` | string | A unique identifier for the end-user | ### GPT Image Parameters | Parameter | Type | Description | |-----------|------|-------------| | `background` | string | Background type: `transparent`, `opaque`, `auto`. Default: `auto` | | `moderation` | string | Moderation level: `low`, `auto`. Default: `auto` | | `output_format` | string | Output format: `png`, `jpeg`, `webp`. Default: `png` | | `output_compression` | integer | Compression level (0-100%) for `jpeg`/`webp` output formats. Default: `100` | > **Note:** GPT image models always return base64-encoded images (`b64_json`). The `response_format` parameter with `url` option is not supported for these models. ### Size Options | Model | Supported Sizes | |-------|-----------------| | `gpt-image-2` | `1024x1024`, `1536x1024`, `1024x1536`, `auto` | ## Example Usage ### Python ```python import base64 from openai import OpenAI client = OpenAI( api_key="sk-your-api-key", base_url="https://api.apertis.ai/v1" ) response = client.images.generate( model="gpt-image-2", prompt="A white siamese cat", n=1, size="1024x1024" ) # GPT image models return base64 with open("image.png", "wb") as f: f.write(base64.b64decode(response.data[0].b64_json)) ``` ### JavaScript ```javascript import OpenAI from 'openai'; import fs from 'fs'; const client = new OpenAI({ apiKey: 'sk-your-api-key', baseURL: 'https://api.apertis.ai/v1' }); const response = await client.images.generate({ model: 'gpt-image-2', prompt: 'A white siamese cat', n: 1, size: '1024x1024' }); // GPT image models return base64 fs.writeFileSync('image.png', Buffer.from(response.data[0].b64_json, 'base64')); ``` ### With Transparent Background ```python response = client.images.generate( model="gpt-image-2", prompt="A logo of a blue bird on transparent background", n=1, size="1024x1024", background="transparent" ) ``` ## Response Format ### URL Response ```json { "created": 1699000000, "data": [ { "url": "https://...", "revised_prompt": "A cute baby sea otter floating on its back..." } ] } ``` ### GPT Image Response (base64) ```json { "created": 1699000000, "data": [ { "b64_json": "iVBORw0KGgoAAAANSUhEUgAA..." } ], "usage": { "total_tokens": 100, "input_tokens": 50, "output_tokens": 50 } } ``` ### Response Fields | Field | Type | Description | |-------|------|-------------| | `created` | integer | Unix timestamp of when the image was created | | `data` | array | Array of generated image objects | | `data[].url` | string | URL of the generated image, for models that return URLs (valid for 60 minutes) | | `data[].b64_json` | string | Base64-encoded image (GPT image models, or when `response_format` is `b64_json`) | | `data[].revised_prompt` | string | The prompt used to generate the image (may be revised by the model) | | `usage` | object | Token usage information (GPT image models only) | | `usage.total_tokens` | integer | Total tokens used | | `usage.input_tokens` | integer | Input tokens used | | `usage.output_tokens` | integer | Output tokens used | ## Supported Models | Model | Description | |-------|-------------| | `gpt-image-2` | OpenAI image generation and editing, with transparent background support | ## Image Edits ``` POST /v1/images/edits ``` The Image Edits endpoint allows you to edit or extend existing images using GPT image models such as `gpt-image-2`. ### HTTP Request ```bash curl https://api.apertis.ai/v1/images/edits \ -H "Authorization: Bearer " \ -F "image=@original.png" \ -F "prompt=Add a rainbow in the sky" \ -F "model=gpt-image-2" \ -F "size=1024x1024" ``` ### Parameters | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `image` | file | Yes | The image to edit. PNG, WebP, or JPG under 50MB for GPT image models | | `prompt` | string | Yes | A text description of the desired edit. Max 32,000 characters for GPT image models | | `mask` | file | No | Mask image indicating transparent areas to edit. PNG under 4MB | | `model` | string | No | Model to use, e.g. `gpt-image-2`. Always set it: when omitted, the gateway falls back to `gpt-image-1`, which is no longer offered | | `n` | integer | No | Number of images to generate (1-10). Default: 1 | | `size` | string | No | Size: `1024x1024`, `1536x1024`, `1024x1536`, `auto`. Default: `auto` | | `quality` | string | No | Quality: `high`, `medium`, `low`, `auto`. Default: `auto` | | `response_format` | string | No | Response format: `url`, `b64_json`. GPT image models always return base64 | | `user` | string | No | A unique identifier for the end-user | ### GPT Image Edit Parameters | Parameter | Type | Description | |-----------|------|-------------| | `background` | string | Background type: `transparent`, `opaque`, `auto`. Default: `auto` | | `moderation` | string | Moderation level: `low`, `auto`. Default: `auto` | | `output_format` | string | Output format: `png`, `jpeg`, `webp`. Default: `png` | | `output_compression` | integer | Compression level (0-100%) for `jpeg`/`webp` output formats. Default: `100` | ### Example Usage #### Python ```python import base64 from openai import OpenAI client = OpenAI( api_key="sk-your-api-key", base_url="https://api.apertis.ai/v1" ) response = client.images.edit( model="gpt-image-2", image=open("original.png", "rb"), prompt="Add a sunset in the background", n=1, size="1024x1024" ) # GPT image models return base64 with open("image.png", "wb") as f: f.write(base64.b64decode(response.data[0].b64_json)) ``` #### JavaScript ```javascript import OpenAI from 'openai'; import fs from 'fs'; const client = new OpenAI({ apiKey: 'sk-your-api-key', baseURL: 'https://api.apertis.ai/v1' }); const response = await client.images.edit({ model: 'gpt-image-2', image: fs.createReadStream('original.png'), prompt: 'Add a sunset in the background', n: 1, size: '1024x1024' }); // GPT image models return base64 fs.writeFileSync('image.png', Buffer.from(response.data[0].b64_json, 'base64')); ``` #### With Mask for Inpainting ```python response = client.images.edit( model="gpt-image-2", image=open("original.png", "rb"), mask=open("mask.png", "rb"), prompt="Replace the masked area with a beautiful garden", n=1, size="1024x1024" ) ``` ## Related Topics - [Chat Completions](/api/text-generation/chat-completions) - Text generation with chat models - [Audio](/api/audio-video/audio) - Speech-to-text and text-to-speech - [Models](/api/utilities/models) - List available models --- Source: https://docs.apertis.ai/api/audio-video/audio # Audio API The Audio API enables speech-to-text transcription, audio translation, text-to-speech synthesis, and audio-input chat completions. This reference covers all audio-related endpoints. ## Overview | Endpoint | Description | |----------|-------------| | `/v1/audio/transcriptions` | Convert audio to text | | `/v1/audio/translations` | Translate audio to English text | | `/v1/audio/speech` | Convert text to audio | | `/v1/chat/completions` | Reason over audio input (`gpt-4o-audio-preview`) | > **Info: PAYG token required** > > Audio endpoints — and any chat completion request that uses an audio modality — require a **Pay-As-You-Go (PAYG) token**. Subscription tokens (`sk-sub_...`) currently receive HTTP 403 with error code `audio_requires_payg`. Subscription quota support for audio is on the roadmap; until then, generate a PAYG key from the dashboard to use audio features. ## Speech to Text (Transcription) Convert audio files to text in the original language. ### Endpoint ``` POST /v1/audio/transcriptions ``` ### Request **Headers:** ``` Authorization: Bearer sk-your-api-key Content-Type: multipart/form-data ``` **Parameters:** | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `file` | file | Yes | Audio file (max 25 MB) | | `model` | string | Yes | Transcription model — see [Supported Transcription Models](#supported-transcription-models) | | `language` | string | No | Language code (ISO 639-1) | | `prompt` | string | No | Context or spelling hints | | `response_format` | string | No | `json`, `text`, `srt`, `vtt`, `verbose_json` (whisper-* only) — `gpt-4o-(mini-)transcribe` always returns `json` | | `temperature` | number | No | Sampling temperature (0-1, default 0) | #### Supported Transcription Models | Model | Best for | Notes | |-------|----------|-------| | `whisper-1` | General-purpose, multilingual | OpenAI's reliable baseline | | `whisper-large-v3` | Higher accuracy than whisper-1 | | | `whisper-large-v3-turbo` | Fastest, lowest cost in the whisper family | | | `gpt-4o-mini-transcribe` | Cheap GPT-4o-grade transcription | Returns `json` only | | `gpt-4o-transcribe` | Highest accuracy, especially Chinese / noisy audio | Returns `json` only — **recommended default for CJK content** | ### Supported Audio Formats - `flac` - `mp3` - `mp4` - `mpeg` - `mpga` - `m4a` - `ogg` - `wav` - `webm` ### Example: Basic Transcription **cURL:** ```bash curl https://api.apertis.ai/v1/audio/transcriptions \ -H "Authorization: Bearer sk-your-api-key" \ -F file="@audio.mp3" \ -F model="whisper-1" ``` **Python:** ```python from openai import OpenAI client = OpenAI( api_key="sk-your-api-key", base_url="https://api.apertis.ai/v1" ) audio_file = open("audio.mp3", "rb") transcript = client.audio.transcriptions.create( model="whisper-1", file=audio_file ) print(transcript.text) ``` **Node.js:** ```javascript import OpenAI from 'openai'; import fs from 'fs'; const client = new OpenAI({ apiKey: 'sk-your-api-key', baseURL: 'https://api.apertis.ai/v1' }); const transcript = await client.audio.transcriptions.create({ model: 'whisper-1', file: fs.createReadStream('audio.mp3') }); console.log(transcript.text); ``` ### Response **JSON format (default):** ```json { "text": "Hello, this is a transcription of the audio file." } ``` **Verbose JSON format:** ```json { "task": "transcribe", "language": "english", "duration": 5.5, "text": "Hello, this is a transcription of the audio file.", "segments": [ { "id": 0, "start": 0.0, "end": 2.5, "text": "Hello, this is", "tokens": [50364, 2425, 11, 341, 307], "temperature": 0.0, "avg_logprob": -0.25, "compression_ratio": 1.2, "no_speech_prob": 0.01 } ] } ``` ### Example: With Language Hint Improve accuracy by specifying the language: ```python transcript = client.audio.transcriptions.create( model="whisper-1", file=audio_file, language="zh" # Chinese ) ``` ### Example: With Spelling Hints Provide context for uncommon words: ```python transcript = client.audio.transcriptions.create( model="whisper-1", file=audio_file, prompt="Apertis, API, OpenAI, GPT-4o" # Spelling hints ) ``` ### Example: SRT Subtitles Generate subtitle files: ```python transcript = client.audio.transcriptions.create( model="whisper-1", file=audio_file, response_format="srt" ) # Save as .srt file with open("subtitles.srt", "w") as f: f.write(transcript) ``` ## Audio Translation Translate audio from any language to English text. ### Endpoint ``` POST /v1/audio/translations ``` ### Request **Parameters:** | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `file` | file | Yes | Audio file (max 25 MB) | | `model` | string | Yes | `whisper-1`, `whisper-large-v3`, or `whisper-large-v3-turbo` (translation is whisper-only) | | `prompt` | string | No | Context hints | | `response_format` | string | No | Output format | | `temperature` | number | No | Sampling temperature | ### Example **Python:** ```python audio_file = open("french_audio.mp3", "rb") translation = client.audio.translations.create( model="whisper-1", file=audio_file ) print(translation.text) # English translation ``` **cURL:** ```bash curl https://api.apertis.ai/v1/audio/translations \ -H "Authorization: Bearer sk-your-api-key" \ -F file="@french_audio.mp3" \ -F model="whisper-1" ``` ### Response ```json { "text": "Hello, how are you today?" } ``` ## Text to Speech (TTS) Convert text to natural-sounding audio. ### Endpoint ``` POST /v1/audio/speech ``` ### Request **Headers:** ``` Authorization: Bearer sk-your-api-key Content-Type: application/json ``` **Parameters:** | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `model` | string | Yes | TTS model — see [Supported TTS Models](#supported-tts-models) | | `input` | string | Yes | Text to convert (max 4096 chars) | | `voice` | string | Yes | Voice to use | | `response_format` | string | No | `mp3`, `opus`, `aac`, `flac`, `wav`, `pcm` | | `speed` | number | No | Speed (0.25-4.0, default 1.0) | | `instructions` | string | No | Tone / accent / pacing prompt — only honoured by `gpt-4o-mini-tts-2025-12-15` | #### Supported TTS Models | Model | Best for | Notes | |-------|----------|-------| | `gpt-4o-mini-tts-2025-12-15` | Steerable voice (instruction-following) | Use the `instructions` field to set tone, accent, pacing | | `gemini-3.1-flash-tts-preview` | Multilingual TTS | Voices: `Aoede`, `Charon`, `Fenrir`, `Kore`, `Puck` | ### Available Voices | Voice | Description | |-------|-------------| | `alloy` | Neutral, balanced | | `echo` | Warm, conversational | | `fable` | Expressive, storytelling | | `onyx` | Deep, authoritative | | `nova` | Energetic, youthful | | `shimmer` | Clear, pleasant | These voices apply to `gpt-4o-mini-tts-2025-12-15`. `gemini-3.1-flash-tts-preview` accepts only `Aoede`, `Charon`, `Fenrir`, `Kore` and `Puck`; any other voice returns HTTP 400 `voice_not_supported`. ### Example: Basic TTS **Python:** ```python response = client.audio.speech.create( model="gpt-4o-mini-tts-2025-12-15", voice="alloy", input="Hello! Welcome to Apertis API documentation." ) # Save to file response.stream_to_file("output.mp3") ``` **Node.js:** ```javascript const response = await client.audio.speech.create({ model: 'gpt-4o-mini-tts-2025-12-15', voice: 'alloy', input: 'Hello! Welcome to Apertis API documentation.' }); const buffer = Buffer.from(await response.arrayBuffer()); fs.writeFileSync('output.mp3', buffer); ``` **cURL:** ```bash curl https://api.apertis.ai/v1/audio/speech \ -H "Authorization: Bearer sk-your-api-key" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-4o-mini-tts-2025-12-15", "voice": "alloy", "input": "Hello! Welcome to Apertis API documentation." }' \ --output output.mp3 ``` ### Response Binary audio data in the requested format. Default is `mp3`. ### Example: High-Quality Audio ```python response = client.audio.speech.create( model="gpt-4o-mini-tts-2025-12-15", voice="nova", input="This is lossless audio.", response_format="flac" # Lossless format ) ``` ### Example: Adjusting Speed ```python # Slower speech response = client.audio.speech.create( model="gpt-4o-mini-tts-2025-12-15", voice="onyx", input="Speaking slowly and clearly.", speed=0.75 ) # Faster speech response = client.audio.speech.create( model="gpt-4o-mini-tts-2025-12-15", voice="echo", input="Speaking quickly!", speed=1.5 ) ``` ### Streaming TTS For real-time audio streaming: ```python with client.audio.speech.with_streaming_response.create( model="gpt-4o-mini-tts-2025-12-15", voice="alloy", input="Streaming audio in real-time..." ) as response: for chunk in response.iter_bytes(): # Process audio chunks as they arrive audio_player.play(chunk) ``` ## Audio in Chat Completions `gpt-4o-audio-preview` accepts audio input (and optionally produces audio output) through the standard `/v1/chat/completions` endpoint. This unlocks voice-agent use cases where the model reasons over the audio directly rather than passing through a transcription step. ### Endpoint ``` POST /v1/chat/completions ``` ### Request Use the standard chat completions schema with three additional fields: - `modalities` — array of output modalities, e.g. `["text", "audio"]` - `audio` — output audio config: `{"voice": "alloy", "format": "wav"}` - A user message whose `content` array includes an `input_audio` block with `{data: , format: <"wav"|"mp3">}` ### Example **Python (openai SDK):** ```python import base64 from openai import OpenAI client = OpenAI(api_key="sk-your-payg-api-key", base_url="https://api.apertis.ai/v1") with open("question.wav", "rb") as f: audio_b64 = base64.b64encode(f.read()).decode() response = client.chat.completions.create( model="gpt-4o-audio-preview", modalities=["text", "audio"], audio={"voice": "alloy", "format": "wav"}, messages=[ { "role": "user", "content": [ {"type": "text", "text": "What does this recording say, and how should I reply?"}, {"type": "input_audio", "input_audio": {"data": audio_b64, "format": "wav"}}, ], } ], ) print(response.choices[0].message.content) # Spoken reply (base64-encoded wav) is on response.choices[0].message.audio ``` **cURL:** ```bash AUDIO_B64=$(base64 -i question.wav) curl https://api.apertis.ai/v1/chat/completions \ -H "Authorization: Bearer sk-your-payg-api-key" \ -H "Content-Type: application/json" \ -d "{ \"model\": \"gpt-4o-audio-preview\", \"modalities\": [\"text\", \"audio\"], \"audio\": {\"voice\": \"alloy\", \"format\": \"wav\"}, \"messages\": [{ \"role\": \"user\", \"content\": [ {\"type\": \"text\", \"text\": \"Summarise this clip.\"}, {\"type\": \"input_audio\", \"input_audio\": {\"data\": \"${AUDIO_B64}\", \"format\": \"wav\"}} ] }] }" ``` ### Notes - Subscription tokens are blocked at the gateway with HTTP 403 / `audio_requires_payg`. Use a PAYG token. - Streaming (`stream: true`) is not yet supported for audio output — request a non-streaming response. - Other adaptors that route through OpenAI-format channels (e.g., OpenRouter) inherit the same content-block shape. ## Supported Languages ### Transcription Languages Whisper supports transcription in 50+ languages: | Language | Code | Language | Code | |----------|------|----------|------| | English | en | Chinese | zh | | Spanish | es | Japanese | ja | | French | fr | Korean | ko | | German | de | Arabic | ar | | Italian | it | Hindi | hi | | Portuguese | pt | Russian | ru | [Full list of supported languages](https://platform.openai.com/docs/guides/speech-to-text/supported-languages) ### TTS Languages Text-to-speech primarily supports: - English (best quality) - Most major languages (good quality) > **Note** > > While TTS can handle multiple languages, voice quality is optimized for English. ## Best Practices ### Audio Quality For best transcription results: 1. **Use high-quality audio** - 16kHz or higher sample rate 2. **Minimize background noise** - Clear recordings work best 3. **Use appropriate format** - MP3 or WAV recommended 4. **Keep files under 25MB** - Split longer recordings ### Optimizing TTS 1. **Use punctuation** - Helps with natural pauses 2. **Break long text** - Split into sentences for better pacing 3. **Choose appropriate voice** - Match voice to content type 4. **Adjust speed** - Slower for clarity, faster for efficiency ### Long Audio Handling For files larger than 25MB: ```python from pydub import AudioSegment # Split audio into 10-minute chunks audio = AudioSegment.from_file("long_audio.mp3") chunk_length = 10 * 60 * 1000 # 10 minutes in milliseconds chunks = [audio[i:i+chunk_length] for i in range(0, len(audio), chunk_length)] transcripts = [] for i, chunk in enumerate(chunks): chunk.export(f"chunk_{i}.mp3", format="mp3") with open(f"chunk_{i}.mp3", "rb") as f: result = client.audio.transcriptions.create( model="whisper-1", file=f ) transcripts.append(result.text) full_transcript = " ".join(transcripts) ``` ## Error Handling ### Common Errors | Error | Cause | Solution | |-------|-------|----------| | `Invalid file format` | Unsupported audio format | Convert to MP3/WAV | | `File too large` | Exceeds 25MB limit | Split into chunks | | `Invalid model` | Model not available | Check model name | | `Rate limit exceeded` | Too many requests | Implement backoff | ### Error Response Example ```json { "error": { "message": "Invalid file format. Supported formats: mp3, mp4, mpeg, mpga, m4a, wav, webm", "type": "invalid_request_error", "code": "invalid_file_format" } } ``` ## Pricing For current model pricing, please visit the [Models page](https://apertis.ai/models). ## Related Topics - [Chat Completions](/api/text-generation/chat-completions) - Text generation API - [Embeddings](/api/embeddings/embeddings-api) - Text embeddings API - [Rate Limits](/billing/rate-limits) - Request limits - [Error Codes](/help/error-codes) - Error reference --- Source: https://docs.apertis.ai/api/audio-video/video # Video API > **Caution: Not currently available** > > Video generation is not available on `api.apertis.ai`. Requests to `/v1/video/create`, `/v1/video/query`, `/v1/videos` (including its `/{id}`, `/{id}/content` and `/{id}/remix` routes) and `/sora/v1/characters` return HTTP 404 `Invalid URL` (checked 2026-10-06). The [model catalog](/models) lists Veo models in its video category, but no API endpoint serves them at the moment. ## Video understanding To have a model analyse a video rather than generate one, send the video to a video-capable chat model through Chat Completions. See [Video understanding with the Python SDK](/api/sdks/python-sdk/video). --- Source: https://docs.apertis.ai/api/embeddings/guide # Embedding API Apertis provides the Embedding API for developers to convert text into vectors and find similar text through vector search. ## Usage (Example in Python) ```python import http.client import json conn = http.client.HTTPSConnection("api.apertis.ai") payload = json.dumps({ "model": "jina-embeddings-v3", "input": "The food was delicious and the waiter..." }) headers = { 'Authorization': 'Bearer ', 'Content-Type': 'application/json' } conn.request("POST", "/v1/embeddings", payload, headers) res = conn.getresponse() data = res.read() print(data.decode("utf-8")) ``` ## Parameters - `model`: The model to use, currently supports `jina-embeddings-v3`, `jina-embeddings-v4`, `jina-embeddings-v5-text-small`, `jina-embeddings-v5-text-nano`, `jina-clip-v1`, `jina-colbert-v2` from **Jina AI**, `mistral-embed-2312` from **Mistral AI** and `gemini-embedding-2-preview` from **Google**. See [Models](/models) for the current list. - `input`: The text to convert - `APERTIS_API_KEY`: Your API key ## Jina AI Embedding Model Usage (Example in Python) ```bash curl -X POST "https://api.apertis.ai/v1/embeddings" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer " \ -d '{ "model": "jina-embeddings-v3", "input": ["你好,世界", "Hello, World"], "task": "retrieval.passage" }' ``` ### Parameters - `model`: The model to use, currently supports `jina-embeddings-v3`, `jina-embeddings-v4`, `jina-embeddings-v5-text-small`, `jina-embeddings-v5-text-nano`, `jina-embeddings-v2-base-de`, `jina-embeddings-v2-base-es`, `jina-clip-v1`, `jina-colbert-v1-en`, `jina-colbert-v2` from **Jina AI**. - `input`: The text to convert - `task`: The task to use, currently supports `retrieval.query`, `retrieval.passage`, `separation`, `classification`, `text-matching` and `none` from **Jina AI** with model `jina-embeddings-v3`. - `APERTIS_API_KEY`: Your API key --- Source: https://docs.apertis.ai/api/embeddings/embeddings-api # Embedding API Apertis provides the Embedding API for developers to convert text into vectors and find similar text through vector search. ## Usage (Example in Python) ```bash tab="cURL" curl https://api.apertis.ai/v1/embeddings \ -H "Content-Type: application/json" \ -H "Authorization: Bearer " \ -d '{ "model": "jina-embeddings-v3", "input": "The food was delicious and the waiter..." }' ``` ```python tab="Python" from openai import OpenAI client = OpenAI( api_key="", base_url="https://api.apertis.ai/v1" ) response = client.embeddings.create( model="jina-embeddings-v3", input="The food was delicious and the waiter..." ) print(response.data[0].embedding) ``` ```javascript tab="JavaScript" import OpenAI from 'openai'; const client = new OpenAI({ apiKey: '', baseURL: 'https://api.apertis.ai/v1' }); const response = await client.embeddings.create({ model: 'jina-embeddings-v3', input: 'The food was delicious and the waiter...' }); console.log(response.data[0].embedding); ``` ## Parameters - `model`: The model to use, currently supports `jina-embeddings-v3`, `jina-embeddings-v4`, `jina-embeddings-v5-text-small`, `jina-embeddings-v5-text-nano`, `jina-clip-v1`, `jina-colbert-v2` from **Jina AI**, `mistral-embed-2312` from **Mistral AI** and `gemini-embedding-2-preview` from **Google**. See [Models](/models) for the current list. - `input`: The text to convert - `APERTIS_API_KEY`: Your API key --- Source: https://docs.apertis.ai/api/embeddings/rerank # Rerank API > **Caution: Not currently available** > > `POST /v1/rerank` is not available on `api.apertis.ai`; requests return HTTP 404 `Invalid URL` (checked 2026-10-06). To order documents by relevance in a retrieval pipeline, compare embedding vectors from the [Embeddings API](/api/embeddings/embeddings-api) instead. --- Source: https://docs.apertis.ai/api/sdks/ai-sdk-provider # @apertis/ai-sdk-provider Official [Vercel AI SDK](https://sdk.vercel.ai/) Provider for Apertis, enabling access to the models available to your Apertis API key through a standardized AI SDK interface. ## Installation ```bash npm install @apertis/ai-sdk-provider ai ``` Or using other package managers: ```bash # pnpm pnpm add @apertis/ai-sdk-provider ai # yarn yarn add @apertis/ai-sdk-provider ai ``` Use version 3.0.1 or later. Versions 2.0.0 to 3.0.0 fail with `ERR_MODULE_NOT_FOUND` when imported from an ES module. ## Setup 1. [Create an Apertis account](https://apertis.ai/register). 2. Create an API key from [**Apertis Keys**](https://apertis.ai/setting?tab=keys). ### Environment Variable ```bash export APERTIS_API_KEY=sk-your-api-key ``` ### Code Configuration ```typescript import { createApertis } from '@apertis/ai-sdk-provider'; const apertis = createApertis({ apiKey: process.env.APERTIS_API_KEY, }); ``` ## Usage Examples ### Basic Text Generation ```typescript import { apertis } from '@apertis/ai-sdk-provider'; import { generateText } from 'ai'; const { text } = await generateText({ model: apertis('gpt-5.2'), prompt: 'Explain quantum computing in simple terms.', }); console.log(text); ``` ### Streaming ```typescript import { apertis } from '@apertis/ai-sdk-provider'; import { streamText } from 'ai'; const { textStream } = await streamText({ model: apertis('claude-sonnet-4-6'), prompt: 'Write a poem about programming.', }); for await (const chunk of textStream) { process.stdout.write(chunk); } ``` ### Tool Calling (Function Calling) ```typescript import { apertis } from '@apertis/ai-sdk-provider'; import { generateText, tool } from 'ai'; import { z } from 'zod'; const { text } = await generateText({ model: apertis('gpt-5.2'), tools: { weather: tool({ description: 'Get weather information for a location', parameters: z.object({ location: z.string() }), execute: async ({ location }) => `Sunny, 22°C in ${location}`, }), }, prompt: 'What is the weather in Tokyo?', }); ``` ### Embeddings ```typescript import { apertis } from '@apertis/ai-sdk-provider'; import { embed, embedMany } from 'ai'; // Single embedding const { embedding } = await embed({ model: apertis.textEmbeddingModel('jina-embeddings-v3'), value: 'Hello world', }); // Batch embeddings const { embeddings } = await embedMany({ model: apertis.textEmbeddingModel('jina-embeddings-v3', { dimensions: 256, // Optional: reduce dimensions }), values: ['Hello', 'World'], }); ``` ## Supported Models Access models from multiple providers through Apertis. Model availability changes over time and can vary by key type, so use [`GET /v1/models`](/api/utilities/models) as the source of truth. ### Chat Models - **OpenAI**: `gpt-5.2`, `gpt-5.4-mini` - **Anthropic**: `claude-sonnet-4-6`, `claude-haiku-4.5` - **Google**: `gemini-2.5-flash` The examples above are illustrative. Query the model catalog before pinning an ID in production. ### Embedding Models - `jina-embeddings-v3` - `mistral-embed-2312` - `gemini-embedding-2-preview` ## Context Compression Enable [context compression](/api/text-generation/context-compression) via custom headers to reduce token usage for long conversations: ```typescript import { createApertis } from '@apertis/ai-sdk-provider'; import { generateText } from 'ai'; const apertis = createApertis({ apiKey: process.env.APERTIS_API_KEY, headers: { 'X-Context-Compression': 'on', 'X-Compression-Model': 'gpt-5.4-mini', }, }); const { text } = await generateText({ model: apertis('gpt-5.5'), messages: [ { role: 'user', content: 'Explain distributed systems' }, { role: 'assistant', content: 'Distributed systems are...' }, // ... long conversation history ... { role: 'user', content: 'Summarize the key points' }, ], }); ``` ## Advanced Configuration ```typescript import { createApertis } from '@apertis/ai-sdk-provider'; const apertis = createApertis({ apiKey: 'sk-your-api-key', // API Key baseURL: 'https://api.apertis.ai/v1', // API endpoint headers: { 'X-Custom': 'value' }, // Custom headers }); ``` ## Resources - [GitHub Repository](https://github.com/apertis-ai/apertis-sdk) - [npm Package](https://www.npmjs.com/package/@apertis/ai-sdk-provider) - [Vercel AI SDK Documentation](https://sdk.vercel.ai/) --- Source: https://docs.apertis.ai/api/sdks/agent-sdk # @apertis/agent [`@apertis/agent`](https://www.npmjs.com/package/@apertis/agent) is a TypeScript agent runtime for the Apertis API. `callModel` runs the whole agent loop for you: send messages → the model calls tools → execute them → feed results back → repeat, until a stop condition fires or the model stops calling tools. The differentiator: the `maxCost` stop condition halts on **real measured spend**, not a token estimate. > Migrating from `@openrouter/agent`? The surface is intentionally close — `callModel`, `tool()`, and the stop conditions map across, so most code ports with a one-import swap. GitHub: [apertis-ai/apertis-agent](https://github.com/apertis-ai/apertis-agent) · npm: `@apertis/agent` ## Installation ```bash npm install @apertis/agent zod ``` Set your API key (get one from the [Apertis Dashboard](https://apertis.ai/setting?tab=keys)): ```bash export APERTIS_API_KEY=sk-your-key ``` Requires Node.js 18+. ## Quickstart ```typescript import { callModel, tool, stepCountIs, maxCost, hasToolCall } from "@apertis/agent"; import { z } from "zod"; const getWeather = tool({ name: "get_weather", description: "Get the weather for a city", inputSchema: z.object({ city: z.string() }), execute: async ({ city }) => ({ city, tempC: 21 }), }); const result = callModel({ model: "claude-sonnet-4-6", input: "What's the weather in Taipei? Then say done.", tools: [getWeather], stopWhen: [stepCountIs(10), maxCost(0.5), hasToolCall("done")], // OR — any one stops the loop }); console.log(await result.getText()); const res = await result.getResponse(); console.log("steps:", res.steps.length, "cost: $", res.cost); ``` Tools are validated with their Zod schema at runtime; if the model sends bad arguments, the error is fed back so it can self-correct rather than throwing. ## Streaming ```typescript const result = callModel({ model: "gpt-5.2", input: "Write a haiku." }); for await (const delta of result.getTextStream()) process.stdout.write(delta); ``` Also available: `getReasoningStream()`, `getToolCallsStream()`, `getToolStream()`, `getNewMessagesStream()`, and `getFullResponsesStream()`. ## Stop conditions `stopWhen` combines conditions with **OR** — the loop stops as soon as any returns true. With no `stopWhen`, a `stepCountIs(20)` backstop applies, and an absolute 100-step cap always holds. | Condition | Stops when | |---|---| | `stepCountIs(n)` | the loop has run `n` steps | | `maxTokensUsed(n)` | cumulative total tokens reach `n` | | `maxCost(usd)` | **measured** cumulative cost reaches `usd` | | `hasToolCall(name)` | the model calls the named tool | | `finishReasonIs(reason)` | the latest `finish_reason` matches | ### How `maxCost` measures cost `maxCost` reads your key's `used_quota_usd` from the usage endpoint after each step and tracks the delta from a pre-run baseline — so it reflects **actual billed spend**, not a pricing-table estimate. If cost can't be measured for a step, the loop stops conservatively rather than risk overspend. ## Tool approval (human-in-the-loop) Mark a tool with `requireApproval` to pause the loop before it runs. State is **client-side** — bring your own `StateAccessor` (Redis, a database, files) to survive restarts. ```typescript import { InMemoryStateAccessor } from "@apertis/agent"; const state = new InMemoryStateAccessor(); const deleteFile = tool({ name: "delete_file", inputSchema: z.object({ path: z.string() }), execute: async ({ path }) => `deleted ${path}`, requireApproval: true, }); const run = callModel({ model: "claude-sonnet-4-6", input: "clean up /tmp", tools: [deleteFile], state }); if (await run.requiresApproval()) { const pending = await run.getPendingToolCalls(); // ...ask a human to approve `pending`... const resumed = callModel({ model: "claude-sonnet-4-6", input: "clean up /tmp", tools: [deleteFile], state, approveToolCalls: [pending[0].id], // or rejectToolCalls }); console.log(await resumed.getText()); } ``` A pending call you neither approve nor reject stays pending and the run re-pauses — it never auto-runs a side-effecting tool. ## Configuration ```typescript import { createCallModel } from "@apertis/agent"; const callModel = createCallModel({ apiKey: "sk-...", baseURL: "https://api.apertis.ai/v1", }); ``` Key precedence: `opts.apiKey` → `APERTIS_API_KEY` → `createCallModel` config. The SDK masks keys in all output and redacts them from error messages. ## Format converters `fromChatMessages` / `toChatMessage` (native) and `fromClaudeMessages` / `toClaudeMessage` bridge Anthropic Messages-format history into the chat-completions format the loop uses. ## Links - npm: [`@apertis/agent`](https://www.npmjs.com/package/@apertis/agent) - Source: [github.com/apertis-ai/apertis-agent](https://github.com/apertis-ai/apertis-agent) - Building with the Vercel AI SDK instead? See [@apertis/ai-sdk-provider](/api/sdks/ai-sdk-provider). --- Source: https://docs.apertis.ai/api/sdks/python-sdk/ # Apertis Python SDK Official Python SDK for the Apertis AI API, providing a comprehensive interface to the models available to your Apertis API key through a unified, type-safe library. ## Features - **Sync and Async Support** - Both synchronous and asynchronous clients for flexible integration - **Streaming** - Real-time response streaming for chat completions - **Tool Calling** - Function/tool calling support for building AI agents - **Embeddings** - Text embedding generation with batch processing - **Vision** - Image analysis with multimodal models - **Audio** - Text to speech, transcription and translation (`client.audio`), plus audio input and output in chat - **Video** - Video content analysis capabilities - **Web Search** - Real-time web search with citation support - **Reasoning** - Chain-of-thought reasoning and extended thinking - **Messages API** - Anthropic-native message format, with streaming and extended thinking - **Responses API** - OpenAI Responses API format, with `response.output_text` - **Rerank** - `client.rerank.create()` exists, but the endpoint is not currently available - **Full Type Hints** - Complete type annotations for IDE support - **Automatic Retries** - Built-in retry logic for transient errors ## Installation ```bash pip install apertis ``` The pages below describe `apertis` 0.4.1. Run `pip install -U apertis` to upgrade an older install. ## Setup Get your API Key from [**Apertis**](https://apertis.ai/setting?tab=keys) ### Environment Variable (Recommended) ```bash export APERTIS_API_KEY=sk-your-api-key ``` ### Code Configuration ```python from apertis import Apertis # Uses APERTIS_API_KEY environment variable automatically client = Apertis() # Or provide API key directly client = Apertis(api_key="sk-your-api-key") ``` ## Quick Start ```python from apertis import Apertis def main(): client = Apertis() response = client.chat.completions.create( model="gpt-5.4-mini", messages=[ {"role": "user", "content": "Hello! What can you help me with?"} ] ) print(response.choices[0].message.content) if __name__ == "__main__": main() ``` ## Supported Models Access models from multiple providers through a single API: | Provider | Example Models | |----------|----------------| | OpenAI | `gpt-5.5`, `gpt-5.4-mini`, `o4-mini-high`, `o3-mini-high` | | Anthropic | `claude-sonnet-4-6`, `claude-opus-4-5-20251101`, `claude-haiku-4.5` | | Google | `gemini-3.1-pro-preview`, `gemini-2.5-flash`, `gemini-2.0-flash-001` | | DeepSeek | `deepseek-v3.2`, `deepseek-r1` | | xAI | `grok-4.3`, `grok-4-fast` | | Current catalog | [View models available to your key](/api/utilities/models) | ## Feature Documentation - [Chat Completions](/api/sdks/python-sdk/chat-completions) - Basic chat and text generation - [Streaming](/api/sdks/python-sdk/streaming) - Real-time response streaming - [Tool Calling](/api/sdks/python-sdk/tool-calling) - Function calling for AI agents - [Embeddings](/api/sdks/python-sdk/embeddings) - Text embeddings and batch processing - [Vision](/api/sdks/python-sdk/vision) - Image analysis with multimodal models - [Audio](/api/sdks/python-sdk/audio) - Audio input and output - [Video](/api/sdks/python-sdk/video) - Video content analysis - [Web Search](/api/sdks/python-sdk/web-search) - Real-time search with citations - [Reasoning](/api/sdks/python-sdk/reasoning) - Chain-of-thought and extended thinking - [Messages API](/api/sdks/python-sdk/messages-api) - Anthropic-native format - [Responses API](/api/sdks/python-sdk/responses-api) - OpenAI Responses format - [Rerank](/api/sdks/python-sdk/rerank) - Not currently available - [Async Patterns](/api/sdks/python-sdk/async) - Asynchronous client usage ## Resources - [GitHub Repository](https://github.com/apertis-ai/python-sdk) - [PyPI Package](https://pypi.org/project/apertis) --- Source: https://docs.apertis.ai/api/sdks/python-sdk/chat-completions # Chat Completions Generate text responses using chat-based models with the familiar OpenAI-compatible interface. ## Prerequisites ```bash pip install apertis ``` Get your API Key from [**Apertis**](https://apertis.ai/setting?tab=keys) ## Basic Usage ```python from apertis import Apertis def main(): client = Apertis() response = client.chat.completions.create( model="gpt-5.4-mini", messages=[ {"role": "user", "content": "Explain quantum computing in simple terms."} ] ) print(response.choices[0].message.content) if __name__ == "__main__": main() ``` ## Multi-Turn Conversations ```python from apertis import Apertis def main(): client = Apertis() messages = [ {"role": "system", "content": "You are a helpful Python tutor."}, {"role": "user", "content": "What is a list comprehension?"}, ] response = client.chat.completions.create( model="claude-sonnet-4-6", messages=messages ) print("Assistant:", response.choices[0].message.content) # Continue the conversation messages.append({"role": "assistant", "content": response.choices[0].message.content}) messages.append({"role": "user", "content": "Can you show me an example?"}) response = client.chat.completions.create( model="claude-sonnet-4-6", messages=messages ) print("\nAssistant:", response.choices[0].message.content) if __name__ == "__main__": main() ``` ## Configuration Options ```python from apertis import Apertis def main(): client = Apertis() response = client.chat.completions.create( model="gpt-5.5", messages=[ {"role": "user", "content": "Write a creative story opening."} ], temperature=0.9, # Higher = more creative (0.0 - 2.0) max_tokens=500, # Maximum response length top_p=0.95, # Nucleus sampling frequency_penalty=0.5, # Reduce repetition presence_penalty=0.5, # Encourage new topics ) print(response.choices[0].message.content) if __name__ == "__main__": main() ``` ## Response Format ```python from apertis import Apertis def main(): client = Apertis() response = client.chat.completions.create( model="gpt-5.4-mini", messages=[ {"role": "user", "content": "Hello!"} ] ) # Access response data print(f"Model: {response.model}") print(f"Content: {response.choices[0].message.content}") print(f"Role: {response.choices[0].message.role}") print(f"Finish Reason: {response.choices[0].finish_reason}") # Token usage print(f"\nPrompt Tokens: {response.usage.prompt_tokens}") print(f"Completion Tokens: {response.usage.completion_tokens}") print(f"Total Tokens: {response.usage.total_tokens}") if __name__ == "__main__": main() ``` ## Context Compression Reduce token usage for long conversations by enabling [context compression](/api/text-generation/context-compression): ```python from apertis import Apertis def main(): client = Apertis() response = client.chat.completions.create( model="gpt-5.5", messages=[ {"role": "user", "content": "Explain distributed systems"}, {"role": "assistant", "content": "Distributed systems are..."}, # ... many turns of conversation history ... {"role": "user", "content": "Summarize the key points"} ], compression={ "enabled": True, "strategy": "on", "model": "gpt-5.4-mini" } ) print(response.choices[0].message.content) if __name__ == "__main__": main() ``` ## Supported Models All chat-capable models are supported, including: | Provider | Models | |----------|--------| | OpenAI | `gpt-5.5`, `gpt-5.4-mini`, `o4-mini-high`, `o3-mini-high` | | Anthropic | `claude-sonnet-4-6`, `claude-opus-4-5-20251101`, `claude-haiku-4.5` | | Google | `gemini-3.1-pro-preview`, `gemini-2.5-flash` | | DeepSeek | `deepseek-v3.2`, `deepseek-r1` | | xAI | `grok-4.3`, `grok-4-fast` | [View all models →](/api/utilities/models) ## API Reference | Parameter | Type | Description | |-----------|------|-------------| | `model` | `str` | Model identifier (required) | | `messages` | `list` | Conversation messages (required) | | `temperature` | `float` | Sampling temperature (0.0 - 2.0) | | `max_tokens` | `int` | Maximum tokens to generate | | `top_p` | `float` | Nucleus sampling parameter | | `frequency_penalty` | `float` | Repetition penalty (-2.0 - 2.0) | | `presence_penalty` | `float` | Topic diversity penalty (-2.0 - 2.0) | | `stop` | `list[str]` | Stop sequences | | `n` | `int` | Number of completions to generate | | `max_completion_tokens` | `int` | Token limit including reasoning tokens (`apertis` 0.4.0 or later) | | `compression` | `dict` | [Context compression](/api/text-generation/context-compression) settings | | `thinking` | `dict` | Extended thinking, for example `{"type": "enabled", "budget_tokens": 10000}` | | `extra_body` | `dict` | Sent as a nested `extra_body` object, not merged into the request. The Apertis API currently ignores it, so provider options such as Gemini's `{"google": {...}}` have no effect. Use the named arguments above for top-level fields | --- Source: https://docs.apertis.ai/api/sdks/python-sdk/streaming # Streaming Receive responses in real-time as they are generated, enabling responsive user interfaces and faster time-to-first-token. ## Prerequisites ```bash pip install apertis ``` Get your API Key from [**Apertis**](https://apertis.ai/setting?tab=keys) ## Basic Streaming ```python from apertis import Apertis def main(): client = Apertis() stream = client.chat.completions.create( model="gpt-5.4-mini", messages=[ {"role": "user", "content": "Write a short poem about coding."} ], stream=True ) for chunk in stream: if chunk.choices[0].delta.content: print(chunk.choices[0].delta.content, end="", flush=True) print() # Newline at the end if __name__ == "__main__": main() ``` ## Collecting Streamed Content ```python from apertis import Apertis def main(): client = Apertis() stream = client.chat.completions.create( model="claude-sonnet-4-6", messages=[ {"role": "user", "content": "Explain machine learning in 3 sentences."} ], stream=True ) collected_content = [] for chunk in stream: content = chunk.choices[0].delta.content if content: collected_content.append(content) print(content, end="", flush=True) print() # Full response full_response = "".join(collected_content) print(f"\n--- Collected {len(full_response)} characters ---") if __name__ == "__main__": main() ``` ## Streaming with Context Manager ```python from apertis import Apertis def main(): client = Apertis() with client.chat.completions.create( model="gpt-5.5", messages=[ {"role": "user", "content": "List 5 programming languages and their use cases."} ], stream=True ) as stream: for chunk in stream: if chunk.choices[0].delta.content: print(chunk.choices[0].delta.content, end="", flush=True) print() if __name__ == "__main__": main() ``` ## Async Streaming ```python import asyncio from apertis import AsyncApertis async def main(): client = AsyncApertis() stream = await client.chat.completions.create( model="gpt-5.4-mini", messages=[ {"role": "user", "content": "Write a haiku about Python."} ], stream=True ) async for chunk in stream: if chunk.choices[0].delta.content: print(chunk.choices[0].delta.content, end="", flush=True) print() if __name__ == "__main__": asyncio.run(main()) ``` ## Streaming with Error Handling ```python from apertis import Apertis from apertis import APIError, RateLimitError def main(): client = Apertis() try: stream = client.chat.completions.create( model="gpt-5.4-mini", messages=[ {"role": "user", "content": "Tell me a joke."} ], stream=True ) for chunk in stream: if chunk.choices[0].delta.content: print(chunk.choices[0].delta.content, end="", flush=True) print() except RateLimitError as e: print(f"\nRate limit exceeded. Please wait and retry. {e}") except APIError as e: print(f"\nAPI error occurred: {e}") if __name__ == "__main__": main() ``` ## Stream Options ```python from apertis import Apertis def main(): client = Apertis() stream = client.chat.completions.create( model="gpt-5.5", messages=[ {"role": "user", "content": "Explain quantum entanglement."} ], stream=True, stream_options={"include_usage": True} # Include token usage in final chunk ) for chunk in stream: # The final usage chunk has an empty choices list if chunk.choices and chunk.choices[0].delta.content: print(chunk.choices[0].delta.content, end="", flush=True) # Token usage in final chunk if chunk.usage: print(f"\n\nTokens used: {chunk.usage.total_tokens}") if __name__ == "__main__": main() ``` ## Supported Models All chat models support streaming: | Provider | Models | |----------|--------| | OpenAI | `gpt-5.5`, `gpt-5.4-mini`, `gpt-5.4-nano` | | Anthropic | `claude-sonnet-4-6`, `claude-opus-4-5-20251101`, `claude-haiku-4.5` | | Google | `gemini-3.1-pro-preview`, `gemini-2.5-flash` | | DeepSeek | `deepseek-v3.2` | | xAI | `grok-4.3`, `grok-4-fast` | [View all models →](/api/utilities/models) ## API Reference | Parameter | Type | Description | |-----------|------|-------------| | `stream` | `bool` | Enable streaming (set to `True`) | | `stream_options` | `dict` | Streaming options (e.g., `{"include_usage": True}`) | --- Source: https://docs.apertis.ai/api/sdks/python-sdk/tool-calling # Tool Calling Enable AI models to call functions and interact with external systems, enabling the creation of AI agents and automated workflows. ## Prerequisites ```bash pip install "apertis>=0.4.0" ``` Get your API Key from [**Apertis**](https://apertis.ai/setting?tab=keys) ## Basic Tool Calling ```python import json from apertis import Apertis def get_weather(location: str, unit: str = "celsius") -> str: """Simulated weather function.""" return json.dumps({ "location": location, "temperature": 22, "unit": unit, "conditions": "sunny" }) def main(): client = Apertis() tools = [ { "type": "function", "function": { "name": "get_weather", "description": "Get the current weather in a given location", "parameters": { "type": "object", "properties": { "location": { "type": "string", "description": "The city name, e.g., 'Tokyo'" }, "unit": { "type": "string", "enum": ["celsius", "fahrenheit"], "description": "Temperature unit" } }, "required": ["location"] } } } ] response = client.chat.completions.create( model="gpt-5.5", messages=[ {"role": "user", "content": "What's the weather like in Tokyo?"} ], tools=tools, tool_choice="auto" ) message = response.choices[0].message if message.tool_calls: tool_call = message.tool_calls[0] print(f"Function: {tool_call.function.name}") print(f"Arguments: {tool_call.function.arguments}") # Execute the function args = json.loads(tool_call.function.arguments) result = get_weather(**args) print(f"Result: {result}") if __name__ == "__main__": main() ``` ## Complete Tool Calling Loop ```python import json from apertis import Apertis # Define available functions def get_weather(location: str, unit: str = "celsius") -> str: return json.dumps({"location": location, "temperature": 22, "unit": unit}) def search_products(query: str, max_results: int = 5) -> str: return json.dumps({"query": query, "results": ["Product A", "Product B", "Product C"]}) AVAILABLE_FUNCTIONS = { "get_weather": get_weather, "search_products": search_products, } def main(): client = Apertis() tools = [ { "type": "function", "function": { "name": "get_weather", "description": "Get current weather for a location", "parameters": { "type": "object", "properties": { "location": {"type": "string", "description": "City name"}, "unit": {"type": "string", "enum": ["celsius", "fahrenheit"]} }, "required": ["location"] } } }, { "type": "function", "function": { "name": "search_products", "description": "Search for products in the catalog", "parameters": { "type": "object", "properties": { "query": {"type": "string", "description": "Search query"}, "max_results": {"type": "integer", "description": "Maximum results"} }, "required": ["query"] } } } ] messages = [ {"role": "user", "content": "What's the weather in Paris and find me some umbrellas?"} ] # First API call response = client.chat.completions.create( model="gpt-5.5", messages=messages, tools=tools, tool_choice="auto" ) message = response.choices[0].message messages.append(message.model_dump(exclude_none=True)) # Process tool calls if message.tool_calls: for tool_call in message.tool_calls: function_name = tool_call.function.name function_args = json.loads(tool_call.function.arguments) print(f"Calling: {function_name}({function_args})") # Execute the function function_response = AVAILABLE_FUNCTIONS[function_name](**function_args) # Add function result to messages messages.append({ "role": "tool", "tool_call_id": tool_call.id, "name": function_name, "content": function_response }) # Second API call with function results final_response = client.chat.completions.create( model="gpt-5.5", messages=messages, tools=tools ) print("\nAssistant:", final_response.choices[0].message.content) if __name__ == "__main__": main() ``` ## Forcing Tool Use ```python import json from apertis import Apertis def main(): client = Apertis() tools = [ { "type": "function", "function": { "name": "extract_entities", "description": "Extract named entities from text", "parameters": { "type": "object", "properties": { "people": {"type": "array", "items": {"type": "string"}}, "places": {"type": "array", "items": {"type": "string"}}, "organizations": {"type": "array", "items": {"type": "string"}} }, "required": ["people", "places", "organizations"] } } } ] response = client.chat.completions.create( model="gpt-5.5", messages=[ {"role": "user", "content": "John works at Google in San Francisco with Mary."} ], tools=tools, tool_choice={"type": "function", "function": {"name": "extract_entities"}} ) tool_call = response.choices[0].message.tool_calls[0] entities = json.loads(tool_call.function.arguments) print("Extracted entities:") print(f" People: {entities['people']}") print(f" Places: {entities['places']}") print(f" Organizations: {entities['organizations']}") if __name__ == "__main__": main() ``` ## Parallel Tool Calls ```python import json from apertis import Apertis def main(): client = Apertis() tools = [ { "type": "function", "function": { "name": "get_stock_price", "description": "Get stock price for a symbol", "parameters": { "type": "object", "properties": { "symbol": {"type": "string", "description": "Stock symbol"} }, "required": ["symbol"] } } } ] response = client.chat.completions.create( model="gpt-5.5", messages=[ {"role": "user", "content": "Get the stock prices for AAPL, GOOGL, and MSFT"} ], tools=tools, parallel_tool_calls=True ) message = response.choices[0].message if message.tool_calls: print(f"Model requested {len(message.tool_calls)} parallel tool calls:") for tool_call in message.tool_calls: args = json.loads(tool_call.function.arguments) print(f" - get_stock_price(symbol='{args['symbol']}')") if __name__ == "__main__": main() ``` ## Supported Models Tool calling is supported by: | Provider | Models | |----------|--------| | OpenAI | `gpt-5.5`, `gpt-5.4-mini`, `gpt-5.4-nano` | | Anthropic | `claude-sonnet-4-6`, `claude-opus-4-5-20251101`, `claude-haiku-4.5` | | Google | `gemini-3.1-pro-preview`, `gemini-2.5-flash` | | DeepSeek | `deepseek-v3.2` | [View all models →](/api/utilities/models) ## API Reference | Parameter | Type | Description | |-----------|------|-------------| | `tools` | `list` | List of tool definitions | | `tool_choice` | `str \| dict` | Tool selection: `"auto"`, `"none"`, `"required"`, or specific function | | `parallel_tool_calls` | `bool` | Allow multiple simultaneous tool calls (`apertis` 0.4.0 or later). Forwarded to OpenAI-compatible models; Anthropic and Google models ignore it | --- Source: https://docs.apertis.ai/api/sdks/python-sdk/embeddings # Embeddings Generate vector embeddings for text, enabling semantic search, clustering, and retrieval-augmented generation (RAG) applications. ## Prerequisites ```bash pip install apertis ``` Get your API Key from [**Apertis**](https://apertis.ai/setting?tab=keys) ## Basic Embedding ```python from apertis import Apertis def main(): client = Apertis() response = client.embeddings.create( model="jina-embeddings-v3", input="Machine learning is a subset of artificial intelligence." ) embedding = response.data[0].embedding print(f"Embedding dimensions: {len(embedding)}") print(f"First 5 values: {embedding[:5]}") if __name__ == "__main__": main() ``` ## Batch Embeddings ```python from apertis import Apertis def main(): client = Apertis() texts = [ "Python is a programming language.", "JavaScript runs in the browser.", "Rust is known for memory safety.", "Go is great for concurrent programming." ] response = client.embeddings.create( model="jina-embeddings-v3", input=texts ) for i, data in enumerate(response.data): print(f"Text {i + 1}: {len(data.embedding)} dimensions") print(f"\nTotal tokens used: {response.usage.total_tokens}") if __name__ == "__main__": main() ``` ## Reduced Dimensions ```python from apertis import Apertis def main(): client = Apertis() # Full dimensions full_response = client.embeddings.create( model="jina-embeddings-v3", input="Hello world" ) # Reduced dimensions for efficiency reduced_response = client.embeddings.create( model="jina-embeddings-v3", input="Hello world", dimensions=256 # Reduce from 1024 to 256 ) print(f"Full dimensions: {len(full_response.data[0].embedding)}") print(f"Reduced dimensions: {len(reduced_response.data[0].embedding)}") if __name__ == "__main__": main() ``` ## Semantic Similarity ```python import numpy as np from apertis import Apertis def cosine_similarity(vec1: list, vec2: list) -> float: """Calculate cosine similarity between two vectors.""" a = np.array(vec1) b = np.array(vec2) return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b)) def main(): client = Apertis() texts = [ "I love programming in Python", "Python is my favorite language", "The weather is nice today", ] response = client.embeddings.create( model="jina-embeddings-v3", input=texts ) embeddings = [data.embedding for data in response.data] print("Similarity scores:") print(f" Text 1 vs Text 2: {cosine_similarity(embeddings[0], embeddings[1]):.4f}") print(f" Text 1 vs Text 3: {cosine_similarity(embeddings[0], embeddings[2]):.4f}") print(f" Text 2 vs Text 3: {cosine_similarity(embeddings[1], embeddings[2]):.4f}") if __name__ == "__main__": main() ``` ## Semantic Search ```python import numpy as np from apertis import Apertis def cosine_similarity(vec1: list, vec2: list) -> float: a = np.array(vec1) b = np.array(vec2) return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b)) def main(): client = Apertis() # Document corpus documents = [ "Python is great for data science and machine learning.", "JavaScript is the language of the web.", "Rust provides memory safety without garbage collection.", "Go excels at building concurrent systems.", "TypeScript adds static typing to JavaScript.", ] # Generate embeddings for documents doc_response = client.embeddings.create( model="jina-embeddings-v3", input=documents ) doc_embeddings = [data.embedding for data in doc_response.data] # Search query query = "Which language is best for web development?" query_response = client.embeddings.create( model="jina-embeddings-v3", input=query ) query_embedding = query_response.data[0].embedding # Find most similar documents similarities = [ (i, cosine_similarity(query_embedding, doc_emb)) for i, doc_emb in enumerate(doc_embeddings) ] similarities.sort(key=lambda x: x[1], reverse=True) print(f"Query: {query}\n") print("Results:") for i, (doc_idx, score) in enumerate(similarities[:3], 1): print(f" {i}. [{score:.4f}] {documents[doc_idx]}") if __name__ == "__main__": main() ``` ## Async Batch Processing ```python import asyncio from apertis import AsyncApertis async def main(): client = AsyncApertis() # Large batch of texts texts = [f"Document number {i} about various topics." for i in range(100)] # Process in batches of 20 batch_size = 20 all_embeddings = [] for i in range(0, len(texts), batch_size): batch = texts[i:i + batch_size] response = await client.embeddings.create( model="jina-embeddings-v3", input=batch ) all_embeddings.extend([data.embedding for data in response.data]) print(f"Processed {min(i + batch_size, len(texts))}/{len(texts)} documents") print(f"\nTotal embeddings: {len(all_embeddings)}") if __name__ == "__main__": asyncio.run(main()) ``` ## Supported Models | Model | Dimensions | Description | |-------|------------|-------------| | `jina-embeddings-v3` | 1024 | Multilingual, supports dimension reduction | | `mistral-embed-2312` | 1024 | Mistral AI embeddings | [View all models →](/api/utilities/models) ## API Reference | Parameter | Type | Description | |-----------|------|-------------| | `model` | `str` | Embedding model identifier (required) | | `input` | `str \| list[str]` | Text(s) to embed (required) | | `dimensions` | `int` | Output dimensions (for models that support it) | | `encoding_format` | `str` | Output format: `"float"` or `"base64"` | --- Source: https://docs.apertis.ai/api/sdks/python-sdk/vision # Vision (Images) Analyze images using multimodal models, enabling visual understanding, image description, and visual question answering. ## Prerequisites ```bash pip install apertis ``` Get your API Key from [**Apertis**](https://apertis.ai/setting?tab=keys) ## Basic Image Analysis ```python from apertis import Apertis def main(): client = Apertis() response = client.chat.completions.create( model="gpt-5.5", messages=[ { "role": "user", "content": [ {"type": "text", "text": "What's in this image?"}, { "type": "image_url", "image_url": { "url": "https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg" } } ] } ] ) print(response.choices[0].message.content) if __name__ == "__main__": main() ``` ## Local Image (Base64) ```python import base64 from pathlib import Path from apertis import Apertis def encode_image(image_path: str) -> str: """Encode image to base64.""" with open(image_path, "rb") as image_file: return base64.standard_b64encode(image_file.read()).decode("utf-8") def main(): client = Apertis() image_path = "path/to/your/image.jpg" base64_image = encode_image(image_path) # Determine media type suffix = Path(image_path).suffix.lower() media_types = {".jpg": "jpeg", ".jpeg": "jpeg", ".png": "png", ".gif": "gif", ".webp": "webp"} media_type = media_types.get(suffix, "jpeg") response = client.chat.completions.create( model="gpt-5.5", messages=[ { "role": "user", "content": [ {"type": "text", "text": "Describe this image in detail."}, { "type": "image_url", "image_url": { "url": f"data:image/{media_type};base64,{base64_image}" } } ] } ] ) print(response.choices[0].message.content) if __name__ == "__main__": main() ``` ## Multiple Images ```python from apertis import Apertis def main(): client = Apertis() response = client.chat.completions.create( model="gpt-5.5", messages=[ { "role": "user", "content": [ {"type": "text", "text": "Compare these two images. What are the differences?"}, { "type": "image_url", "image_url": {"url": "https://example.com/image1.jpg"} }, { "type": "image_url", "image_url": {"url": "https://example.com/image2.jpg"} } ] } ] ) print(response.choices[0].message.content) if __name__ == "__main__": main() ``` ## Image Quality Control ```python from apertis import Apertis def main(): client = Apertis() # High detail for complex images response = client.chat.completions.create( model="gpt-5.5", messages=[ { "role": "user", "content": [ {"type": "text", "text": "Read all the text in this document."}, { "type": "image_url", "image_url": { "url": "https://example.com/document.png", "detail": "high" # Use high detail for text/documents } } ] } ] ) print(response.choices[0].message.content) if __name__ == "__main__": main() ``` ## Visual Question Answering ```python from apertis import Apertis def main(): client = Apertis() image_url = "https://upload.wikimedia.org/wikipedia/commons/thumb/4/47/PNG_transparency_demonstration_1.png/300px-PNG_transparency_demonstration_1.png" questions = [ "What objects are in this image?", "What colors do you see?", "Is there any text visible?", ] for question in questions: response = client.chat.completions.create( model="gpt-5.4-mini", messages=[ { "role": "user", "content": [ {"type": "text", "text": question}, {"type": "image_url", "image_url": {"url": image_url}} ] } ] ) print(f"Q: {question}") print(f"A: {response.choices[0].message.content}\n") if __name__ == "__main__": main() ``` ## Streaming with Images ```python from apertis import Apertis def main(): client = Apertis() stream = client.chat.completions.create( model="gpt-5.5", messages=[ { "role": "user", "content": [ {"type": "text", "text": "Describe this image in detail."}, { "type": "image_url", "image_url": { "url": "https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg" } } ] } ], stream=True ) for chunk in stream: if chunk.choices[0].delta.content: print(chunk.choices[0].delta.content, end="", flush=True) print() if __name__ == "__main__": main() ``` ## Supported Models Vision capabilities are available on: | Provider | Models | |----------|--------| | OpenAI | `gpt-5.5`, `gpt-5.4-mini`, `gpt-5.4-nano` | | Anthropic | `claude-sonnet-4-6`, `claude-opus-4-5-20251101`, `claude-haiku-4.5` | | Google | `gemini-3.1-pro-preview`, `gemini-2.5-flash` | [View all models →](/api/utilities/models) ## API Reference ### Image URL Object | Field | Type | Description | |-------|------|-------------| | `url` | `str` | Image URL or base64 data URI | | `detail` | `str` | Detail level: `"auto"`, `"low"`, or `"high"` | ### Supported Formats - JPEG / JPG - PNG - GIF - WebP ### Size Limits - Maximum image size varies by model - Images are automatically resized if needed - Use `detail: "low"` for faster processing of simple images --- Source: https://docs.apertis.ai/api/sdks/python-sdk/audio # Audio Process audio input and generate audio output, enabling voice-based applications, transcription, and text-to-speech. ## Prerequisites ```bash pip install "apertis>=0.4.0" ``` `client.audio` needs `apertis` 0.4.0 or later. Get your API Key from [**Apertis**](https://apertis.ai/setting?tab=keys) ## Speech to Text (Transcription) ```python from apertis import Apertis def main(): client = Apertis() with open("audio.mp3", "rb") as audio_file: response = client.audio.transcriptions.create( model="whisper-1", file=audio_file ) print(response.text) if __name__ == "__main__": main() ``` ## Transcription with Options ```python from apertis import Apertis def main(): client = Apertis() with open("audio.mp3", "rb") as audio_file: response = client.audio.transcriptions.create( model="whisper-1", file=audio_file, language="en", # Specify language for better accuracy prompt="Technical discussion about AI and machine learning", # Context hint response_format="verbose_json", # Get detailed output temperature=0.0 # Lower = more deterministic ) print(f"Text: {response.text}") # verbose_json fields such as segments are kept as plain dicts for segment in response.model_dump().get("segments", []): print(f" [{segment['start']:.2f}s - {segment['end']:.2f}s] {segment['text']}") if __name__ == "__main__": main() ``` ## Translation (Audio to English) ```python from apertis import Apertis def main(): client = Apertis() # Translate non-English audio to English text with open("spanish_audio.mp3", "rb") as audio_file: response = client.audio.translations.create( model="whisper-1", file=audio_file ) print(f"English translation: {response.text}") if __name__ == "__main__": main() ``` ## Text to Speech ```python from apertis import Apertis def main(): client = Apertis() response = client.audio.speech.create( model="gpt-4o-mini-tts-2025-12-15", voice="alloy", input="Hello! Welcome to Apertis AI. How can I help you today?" ) response.write_to_file("output.mp3") print("Audio saved to output.mp3") if __name__ == "__main__": main() ``` ## Voice, Format and Speed ```python from apertis import Apertis def main(): client = Apertis() response = client.audio.speech.create( model="gpt-4o-mini-tts-2025-12-15", voice="nova", # Different voice input="This is text-to-speech audio with a different voice.", response_format="opus", # Opus format for better quality speed=1.0 # Normal speed (0.25 to 4.0) ) response.write_to_file("output.opus") print("Audio saved to output.opus") if __name__ == "__main__": main() ``` ## Streaming Text to Speech `client.audio.speech.create()` returns the whole audio file at once. To write the audio while it is still being generated, use the OpenAI SDK's streaming response pointed at Apertis: ```python import os from openai import OpenAI def main(): client = OpenAI(api_key=os.environ["APERTIS_API_KEY"], base_url="https://api.apertis.ai/v1") with client.audio.speech.with_streaming_response.create( model="gpt-4o-mini-tts-2025-12-15", voice="shimmer", input="This audio is being streamed as it's generated.", ) as response: response.stream_to_file("streamed_output.mp3") print("Streamed audio saved to streamed_output.mp3") if __name__ == "__main__": main() ``` ## Audio in Chat (Multimodal) ```python import base64 from apertis import Apertis def main(): client = Apertis() # Read and encode audio file with open("question.mp3", "rb") as f: audio_data = base64.standard_b64encode(f.read()).decode("utf-8") response = client.chat.completions.create( model="gpt-4o-audio-preview", messages=[ { "role": "user", "content": [ {"type": "text", "text": "Please respond to this audio message:"}, { "type": "input_audio", "input_audio": { "data": audio_data, "format": "mp3" } } ] } ] ) print(response.choices[0].message.content) if __name__ == "__main__": main() ``` ## Batch Transcription ```python import asyncio from pathlib import Path from apertis import AsyncApertis async def transcribe_file(client: AsyncApertis, file_path: str) -> dict: """Transcribe a single audio file.""" with open(file_path, "rb") as f: response = await client.audio.transcriptions.create( model="whisper-1", file=f ) return {"file": file_path, "text": response.text} async def main(): client = AsyncApertis() # List of audio files to process audio_files = list(Path("audio_folder").glob("*.mp3")) # Process files concurrently tasks = [transcribe_file(client, str(f)) for f in audio_files] results = await asyncio.gather(*tasks) for result in results: print(f"\n{result['file']}:") print(f" {result['text'][:100]}...") if __name__ == "__main__": asyncio.run(main()) ``` ## Supported Models ### Transcription/Translation | Model | Description | |-------|-------------| | `whisper-1` | OpenAI Whisper for speech recognition | ### Text to Speech | Model | Description | |-------|-------------| | `gpt-4o-mini-tts-2025-12-15` | Steerable voice; accepts `instructions` for tone and pacing | | `gemini-3.1-flash-tts-preview` | Multilingual TTS | ### Available Voices | Voice | Description | |-------|-------------| | `alloy` | Neutral, balanced | | `echo` | Warm, conversational | | `fable` | Expressive, narrative | | `onyx` | Deep, authoritative | | `nova` | Friendly, upbeat | | `shimmer` | Clear, professional | These voices apply to `gpt-4o-mini-tts-2025-12-15`. `gemini-3.1-flash-tts-preview` accepts only `Aoede`, `Charon`, `Fenrir`, `Kore` and `Puck`; any other voice returns HTTP 400 `voice_not_supported`. ## API Reference ### Transcription Parameters | Parameter | Type | Description | |-----------|------|-------------| | `file` | `file` | Audio file (required) | | `model` | `str` | Model identifier (required) | | `language` | `str` | ISO language code | | `prompt` | `str` | Context hint for transcription | | `response_format` | `str` | `"json"`, `"text"`, `"verbose_json"`, `"srt"`, `"vtt"`. The text formats return a `str` | | `temperature` | `float` | Sampling temperature | | `timestamp_granularities` | `list` | `["word"]` and/or `["segment"]`, with `verbose_json` | `file` accepts a path, an open binary file, `bytes`, or a `(filename, bytes)` tuple. The file name's extension tells the API the audio format. ### Speech Parameters | Parameter | Type | Description | |-----------|------|-------------| | `model` | `str` | TTS model (required) | | `voice` | `str` | Voice selection (required) | | `input` | `str` | Text to convert (required) | | `response_format` | `str` | `"mp3"`, `"opus"`, `"aac"`, `"flac"` | | `speed` | `float` | Speed multiplier (0.25 to 4.0) | | `instructions` | `str` | Tone and pacing instructions, for models that accept them | `speech.create()` returns an object with `.content` (the audio bytes) and `.write_to_file(path)`. ### Supported Audio Formats - MP3 - MP4 - MPEG - MPGA - M4A - WAV - WebM --- Source: https://docs.apertis.ai/api/sdks/python-sdk/video # Video Analyze video content using multimodal models, enabling video understanding, scene analysis, and temporal reasoning. ## Prerequisites ```bash pip install apertis ``` Get your API Key from [**Apertis**](https://apertis.ai/setting?tab=keys) ## Basic Video Analysis ```python from apertis import Apertis def main(): client = Apertis() response = client.chat.completions.create( model="gemini-3.1-pro-preview", messages=[ { "role": "user", "content": [ {"type": "text", "text": "Describe what happens in this video."}, { "type": "video_url", "video_url": { "url": "https://example.com/video.mp4" } } ] } ] ) print(response.choices[0].message.content) if __name__ == "__main__": main() ``` ## Local Video (Base64) ```python import base64 from apertis import Apertis def encode_video(video_path: str) -> str: """Encode video to base64.""" with open(video_path, "rb") as video_file: return base64.standard_b64encode(video_file.read()).decode("utf-8") def main(): client = Apertis() video_path = "path/to/your/video.mp4" base64_video = encode_video(video_path) response = client.chat.completions.create( model="gemini-3.1-pro-preview", messages=[ { "role": "user", "content": [ {"type": "text", "text": "What is happening in this video?"}, { "type": "video_url", "video_url": { "url": f"data:video/mp4;base64,{base64_video}" } } ] } ] ) print(response.choices[0].message.content) if __name__ == "__main__": main() ``` ## Video Question Answering ```python from apertis import Apertis def main(): client = Apertis() video_url = "https://example.com/cooking-video.mp4" questions = [ "What dish is being prepared?", "What ingredients are used?", "How many steps are in this recipe?", "What cooking techniques are demonstrated?" ] for question in questions: response = client.chat.completions.create( model="gemini-3.1-pro-preview", messages=[ { "role": "user", "content": [ {"type": "text", "text": question}, {"type": "video_url", "video_url": {"url": video_url}} ] } ] ) print(f"Q: {question}") print(f"A: {response.choices[0].message.content}\n") if __name__ == "__main__": main() ``` ## Temporal Analysis ```python from apertis import Apertis def main(): client = Apertis() response = client.chat.completions.create( model="gemini-3.1-pro-preview", messages=[ { "role": "user", "content": [ { "type": "text", "text": """Analyze this video and provide: 1. A timeline of key events with approximate timestamps 2. Scene transitions 3. Any significant actions or changes""" }, { "type": "video_url", "video_url": {"url": "https://example.com/event-video.mp4"} } ] } ] ) print(response.choices[0].message.content) if __name__ == "__main__": main() ``` ## Video with Audio Analysis ```python from apertis import Apertis def main(): client = Apertis() response = client.chat.completions.create( model="gemini-3.1-pro-preview", messages=[ { "role": "user", "content": [ { "type": "text", "text": """Analyze both the visual and audio content of this video: 1. What is being shown visually? 2. What is being said or what sounds are present? 3. How do the audio and video relate to each other?""" }, { "type": "video_url", "video_url": {"url": "https://example.com/presentation.mp4"} } ] } ] ) print(response.choices[0].message.content) if __name__ == "__main__": main() ``` ## Video Comparison ```python from apertis import Apertis def main(): client = Apertis() response = client.chat.completions.create( model="gemini-3.1-pro-preview", messages=[ { "role": "user", "content": [ {"type": "text", "text": "Compare these two videos. What are the similarities and differences?"}, {"type": "video_url", "video_url": {"url": "https://example.com/video1.mp4"}}, {"type": "video_url", "video_url": {"url": "https://example.com/video2.mp4"}} ] } ] ) print(response.choices[0].message.content) if __name__ == "__main__": main() ``` ## Streaming Video Analysis ```python from apertis import Apertis def main(): client = Apertis() stream = client.chat.completions.create( model="gemini-3.1-pro-preview", messages=[ { "role": "user", "content": [ {"type": "text", "text": "Provide a detailed scene-by-scene breakdown of this video."}, {"type": "video_url", "video_url": {"url": "https://example.com/movie-clip.mp4"}} ] } ], stream=True ) for chunk in stream: if chunk.choices[0].delta.content: print(chunk.choices[0].delta.content, end="", flush=True) print() if __name__ == "__main__": main() ``` ## Action Recognition ```python from apertis import Apertis def main(): client = Apertis() response = client.chat.completions.create( model="gemini-3.1-pro-preview", messages=[ { "role": "user", "content": [ { "type": "text", "text": """Identify all actions performed in this video. For each action, provide: - Action name - Who/what is performing it - Approximate duration or timestamp""" }, {"type": "video_url", "video_url": {"url": "https://example.com/sports.mp4"}} ] } ] ) print(response.choices[0].message.content) if __name__ == "__main__": main() ``` ## Supported Models Video analysis is available on: | Provider | Models | |----------|--------| | Google | `gemini-3.1-pro-preview`, `gemini-2.5-flash`, `gemini-2.0-flash-001` | | OpenAI | `gpt-5.5` (limited video support) | [View all models →](/api/utilities/models) ## API Reference ### Video URL Object | Field | Type | Description | |-------|------|-------------| | `url` | `str` | Video URL or base64 data URI | ### Supported Formats - MP4 - MOV - AVI - MKV - WebM ### Limitations - Maximum video length varies by model - Large videos may be sampled or truncated - Processing time increases with video length - Some models may not support audio track analysis --- Source: https://docs.apertis.ai/api/sdks/python-sdk/web-search # Web Search Enable AI models to search the web in real-time, providing up-to-date information with citations and source attribution. ## Prerequisites ```bash pip install apertis ``` Get your API Key from [**Apertis**](https://apertis.ai/setting?tab=keys) ## Basic Web Search ```python from apertis import Apertis def main(): client = Apertis() response = client.chat.completions.create( model="gpt-5.5:web", messages=[ {"role": "user", "content": "What are the latest developments in AI?"} ] ) print(response.choices[0].message.content) if __name__ == "__main__": main() ``` ## Web Search with Citations ```python from apertis import Apertis def main(): client = Apertis() response = client.chat.completions.create( model="gpt-5.5:web", messages=[ {"role": "user", "content": "What is the current stock price of Apple?"} ] ) message = response.choices[0].message print("Response:", message.content) # Sources found by web search if response.web_sources: print("\nSources:") for source in response.web_sources: print(f" - {source.title}: {source.url}") if __name__ == "__main__": main() ``` ## Factual Questions ```python from apertis import Apertis def main(): client = Apertis() questions = [ "Who won the latest Super Bowl?", "What is the current population of Tokyo?", "When is the next solar eclipse?", ] for question in questions: response = client.chat.completions.create( model="gpt-5.5:web", messages=[ {"role": "user", "content": question} ] ) print(f"Q: {question}") print(f"A: {response.choices[0].message.content}\n") if __name__ == "__main__": main() ``` ## Research Assistant ```python from apertis import Apertis def main(): client = Apertis() topic = "quantum computing applications in drug discovery" response = client.chat.completions.create( model="gpt-5.5:web", messages=[ { "role": "system", "content": "You are a research assistant. Provide comprehensive, well-cited answers." }, { "role": "user", "content": f"""Research the following topic and provide: 1. Current state of the field 2. Key players and organizations 3. Recent breakthroughs 4. Future outlook Topic: {topic}""" } ] ) print(response.choices[0].message.content) if __name__ == "__main__": main() ``` ## News Summary ```python from apertis import Apertis def main(): client = Apertis() response = client.chat.completions.create( model="gpt-5.5:web", messages=[ { "role": "user", "content": """Summarize today's top technology news. Include: - Company announcements - Product launches - Industry trends Provide sources for each item.""" } ] ) print(response.choices[0].message.content) if __name__ == "__main__": main() ``` ## Streaming with Web Search ```python from apertis import Apertis def main(): client = Apertis() stream = client.chat.completions.create( model="gpt-5.5:web", messages=[ {"role": "user", "content": "What are the current trends in renewable energy?"} ], stream=True ) for chunk in stream: if chunk.choices and chunk.choices[0].delta.content: print(chunk.choices[0].delta.content, end="", flush=True) print() if __name__ == "__main__": main() ``` ## Competitive Analysis ```python from apertis import Apertis def main(): client = Apertis() company = "OpenAI" response = client.chat.completions.create( model="gpt-5.5:web", messages=[ { "role": "user", "content": f"""Provide a competitive analysis for {company}: 1. Recent product announcements 2. Market positioning 3. Key competitors 4. Strategic moves 5. Industry perception Use only current, verifiable information.""" } ] ) print(response.choices[0].message.content) if __name__ == "__main__": main() ``` ## Multi-Turn with Web Search ```python from apertis import Apertis def main(): client = Apertis() messages = [ {"role": "user", "content": "What is the current status of the James Webb Space Telescope?"} ] # First query response = client.chat.completions.create( model="gpt-5.5:web", messages=messages ) print("Assistant:", response.choices[0].message.content) # Follow-up messages.append({"role": "assistant", "content": response.choices[0].message.content}) messages.append({"role": "user", "content": "What are its most recent discoveries?"}) response = client.chat.completions.create( model="gpt-5.5:web", messages=messages ) print("\nAssistant:", response.choices[0].message.content) if __name__ == "__main__": main() ``` ## Supported Models Append `:web` to any non-free model ID, for example: | Provider | Models | |----------|--------| | OpenAI | `gpt-5.5`, `gpt-5.4-mini` | | Anthropic | `claude-sonnet-4-6`, `claude-opus-4-5-20251101` | | Google | `gemini-3.1-pro-preview`, `gemini-2.5-flash` | [View all models →](/api/utilities/models) ## API Reference | Parameter | Type | Description | |-----------|------|-------------| | `model` | `str` | Model ID with the `:web` suffix, for example `gpt-5.5:web` | | `web_results_count` | `int` | Number of search results to use (1-10) | | `web_content_length` | `str` | How much page content to use: `"short"`, `"medium"` or `"full"` | ### Web Source Object Non-streaming responses list the pages used in `response.web_sources`: | Field | Type | Description | |-------|------|-------------| | `title` | `str` | Source page title | | `url` | `str` | Source URL | | `snippet` | `str` | Relevant excerpt (if available) | ## Best Practices 1. **Be specific** - More specific queries yield better search results 2. **Request citations** - Ask the model to cite sources in its response 3. **Use for current events** - Web search is ideal for recent/real-time information 4. **Verify important facts** - Cross-reference critical information 5. **Consider rate limits** - Web search may have additional rate limiting --- Source: https://docs.apertis.ai/api/sdks/python-sdk/reasoning # Reasoning & Extended Thinking Enable chain-of-thought reasoning and extended thinking capabilities for complex problem-solving, analysis, and multi-step tasks. ## Prerequisites ```bash pip install apertis ``` Get your API Key from [**Apertis**](https://apertis.ai/setting?tab=keys) ## Basic Reasoning ```python from apertis import Apertis def main(): client = Apertis() response = client.chat.completions.create( model="gpt-5.5", # OpenAI reasoning model messages=[ {"role": "user", "content": "Solve this step by step: A train travels from A to B at 60 km/h and returns at 40 km/h. What is the average speed for the round trip?"} ] ) print(response.choices[0].message.content) if __name__ == "__main__": main() ``` ## Extended Thinking with Claude ```python from apertis import Apertis def main(): client = Apertis() response = client.chat.completions.create( model="claude-sonnet-4-6", messages=[ {"role": "user", "content": "Analyze the pros and cons of microservices vs monolithic architecture for a startup."} ], thinking={ "type": "enabled", "budget_tokens": 10000 # Token budget for thinking } ) message = response.choices[0].message # Access thinking content if available if hasattr(message, 'thinking') and message.thinking: print("=== Thinking Process ===") print(message.thinking) print("\n=== Final Answer ===") print(message.content) if __name__ == "__main__": main() ``` ## DeepSeek Reasoning ```python from apertis import Apertis def main(): client = Apertis() response = client.chat.completions.create( model="deepseek-r1", messages=[ {"role": "user", "content": "Prove that the square root of 2 is irrational."} ] ) message = response.choices[0].message # DeepSeek reasoning models include reasoning content if hasattr(message, 'reasoning_content') and message.reasoning_content: print("=== Reasoning ===") print(message.reasoning_content) print("\n=== Answer ===") print(message.content) if __name__ == "__main__": main() ``` ## Complex Problem Solving ```python from apertis import Apertis def main(): client = Apertis() problem = """ A company has 3 warehouses (W1, W2, W3) and 4 retail stores (R1, R2, R3, R4). The supply at each warehouse is: W1=100, W2=150, W3=200 units. The demand at each store is: R1=80, R2=120, R3=150, R4=100 units. Transportation costs per unit: R1 R2 R3 R4 W1 8 6 10 9 W2 9 12 13 7 W3 14 9 16 5 Find the optimal transportation plan to minimize total cost. """ response = client.chat.completions.create( model="gpt-5.5", messages=[ {"role": "user", "content": problem} ] ) print(response.choices[0].message.content) if __name__ == "__main__": main() ``` ## Code Analysis with Reasoning ````python from apertis import Apertis def main(): client = Apertis() code = ''' def mystery(arr): n = len(arr) for i in range(n): for j in range(0, n-i-1): if arr[j] > arr[j+1]: arr[j], arr[j+1] = arr[j+1], arr[j] return arr ''' response = client.chat.completions.create( model="o4-mini-high", messages=[ { "role": "user", "content": f"""Analyze this code: 1. What algorithm is this? 2. What is its time complexity? Explain your reasoning. 3. What is its space complexity? 4. How could it be optimized? ```python {code} ```""" } ] ) print(response.choices[0].message.content) if __name__ == "__main__": main() ```` ## Mathematical Proofs ```python from apertis import Apertis def main(): client = Apertis() response = client.chat.completions.create( model="gpt-5.5", messages=[ { "role": "user", "content": "Prove by induction that the sum of the first n positive integers is n(n+1)/2" } ] ) print(response.choices[0].message.content) if __name__ == "__main__": main() ``` ## Logic Puzzles ```python from apertis import Apertis def main(): client = Apertis() puzzle = """ Five friends (Alice, Bob, Carol, Dave, Eve) each have a different favorite color (red, blue, green, yellow, purple) and a different pet (cat, dog, fish, bird, rabbit). Clues: 1. Alice's favorite color is not red or blue. 2. The person with the cat likes yellow. 3. Bob has a dog. 4. Carol's favorite color is blue. 5. Dave doesn't have the rabbit. 6. Eve's favorite color is red. 7. The person who likes green has a fish. 8. Alice doesn't have the bird. Who has which pet and favorite color? """ response = client.chat.completions.create( model="gpt-5.5", messages=[ {"role": "user", "content": puzzle} ] ) print(response.choices[0].message.content) if __name__ == "__main__": main() ``` ## Streaming Reasoning (Where Supported) ```python from apertis import Apertis def main(): client = Apertis() stream = client.chat.completions.create( model="deepseek-r1", messages=[ {"role": "user", "content": "What is 25% of 80? Walk through your thinking."} ], stream=True ) for chunk in stream: if chunk.choices[0].delta.content: print(chunk.choices[0].delta.content, end="", flush=True) print() if __name__ == "__main__": main() ``` ## Supported Models ### OpenAI Reasoning Models | Model | Description | |-------|-------------| | `gpt-5.5` | Full reasoning capabilities | | `o4-mini-high` | Faster, more cost-effective | | `o3-mini-high` | Compact reasoning model | ### DeepSeek Reasoning Models | Model | Description | |-------|-------------| | `deepseek-r1` | Chain-of-thought reasoning | ### Claude Extended Thinking | Model | Description | |-------|-------------| | `claude-sonnet-4-6` | Extended thinking via `thinking` parameter | | `claude-opus-4-5-20251101` | Extended thinking via `thinking` parameter | [View all models →](/api/utilities/models) ## API Reference ### Reasoning Parameters | Parameter | Type | Description | |-----------|------|-------------| | `model` | `str` | Reasoning-capable model (required) | ### Claude Extended Thinking ```python thinking={ "type": "enabled", "budget_tokens": 10000 } ``` | Field | Type | Description | |-------|------|-------------| | `type` | `str` | `"enabled"` to enable thinking | | `budget_tokens` | `int` | Maximum tokens for thinking process | ## Best Practices 1. **Use appropriate models** - Reasoning models are designed for complex problems 2. **Be specific** - Clearer problems yield better reasoning 3. **Request step-by-step** - Ask for explicit reasoning steps when needed 4. **Consider cost** - Reasoning models may use more tokens 5. **Verify outputs** - Review reasoning steps for accuracy --- Source: https://docs.apertis.ai/api/sdks/python-sdk/messages-api # Messages API Use Anthropic's native message format for interacting with Claude models, providing access to Claude-specific features and optimal performance. ## Prerequisites ```bash pip install "apertis>=0.4.0" ``` Get your API Key from [**Apertis**](https://apertis.ai/setting?tab=keys) ## Basic Usage ```python from apertis import Apertis def main(): client = Apertis() response = client.messages.create( model="claude-sonnet-4-6", max_tokens=1024, messages=[ {"role": "user", "content": "Hello, Claude!"} ] ) print(response.content[0].text) if __name__ == "__main__": main() ``` ## System Prompts ```python from apertis import Apertis def main(): client = Apertis() response = client.messages.create( model="claude-sonnet-4-6", max_tokens=1024, system="You are a helpful coding assistant specializing in Python.", messages=[ {"role": "user", "content": "How do I read a JSON file?"} ] ) print(response.content[0].text) if __name__ == "__main__": main() ``` ## Multi-Turn Conversations ```python from apertis import Apertis def main(): client = Apertis() messages = [ {"role": "user", "content": "What is recursion?"}, ] response = client.messages.create( model="claude-sonnet-4-6", max_tokens=1024, messages=messages ) print("Claude:", response.content[0].text) # Continue conversation messages.append({"role": "assistant", "content": response.content[0].text}) messages.append({"role": "user", "content": "Can you show me an example in Python?"}) response = client.messages.create( model="claude-sonnet-4-6", max_tokens=1024, messages=messages ) print("\nClaude:", response.content[0].text) if __name__ == "__main__": main() ``` ## Streaming `stream=True` returns an iterator of Anthropic stream events (`apertis` 0.4.0 or later): ```python from apertis import Apertis def main(): client = Apertis() stream = client.messages.create( model="claude-sonnet-4-6", max_tokens=1024, messages=[ {"role": "user", "content": "Write a short poem about coding."} ], stream=True, ) for event in stream: if event.type == "content_block_delta" and event.delta.type == "text_delta": print(event.delta.text, end="", flush=True) print() if __name__ == "__main__": main() ``` ## Vision with Messages API ```python import base64 from apertis import Apertis def main(): client = Apertis() # From URL response = client.messages.create( model="claude-sonnet-4-6", max_tokens=1024, messages=[ { "role": "user", "content": [ { "type": "image", "source": { "type": "url", "url": "https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg" } }, { "type": "text", "text": "What do you see in this image?" } ] } ] ) print(response.content[0].text) if __name__ == "__main__": main() ``` ## Tool Use (Function Calling) ```python import json from apertis import Apertis def main(): client = Apertis() tools = [ { "name": "get_weather", "description": "Get the current weather in a given location", "input_schema": { "type": "object", "properties": { "location": { "type": "string", "description": "The city and state, e.g. San Francisco, CA" }, "unit": { "type": "string", "enum": ["celsius", "fahrenheit"], "description": "The unit of temperature" } }, "required": ["location"] } } ] response = client.messages.create( model="claude-sonnet-4-6", max_tokens=1024, tools=tools, messages=[ {"role": "user", "content": "What's the weather like in Tokyo?"} ] ) for content in response.content: if content.type == "tool_use": print(f"Tool: {content.name}") print(f"Input: {json.dumps(content.input, indent=2)}") elif content.type == "text": print(f"Text: {content.text}") if __name__ == "__main__": main() ``` ## Extended Thinking Pass `thinking` to get the model's reasoning as `thinking` blocks before the answer (`apertis` 0.4.0 or later): ```python from apertis import Apertis def main(): client = Apertis() response = client.messages.create( model="claude-sonnet-4-6", max_tokens=16000, thinking={ "type": "enabled", "budget_tokens": 10000 }, messages=[ {"role": "user", "content": "Analyze the trade-offs between SQL and NoSQL databases for a social media application."} ] ) for content in response.content: if content.type == "thinking": print("=== Thinking ===") print(content.thinking) print() elif content.type == "text": print("=== Response ===") print(content.text) if __name__ == "__main__": main() ``` With `stream=True`, thinking arrives as `thinking_delta` events before the `text_delta` events. ## Response Metadata ```python from apertis import Apertis def main(): client = Apertis() response = client.messages.create( model="claude-sonnet-4-6", max_tokens=1024, messages=[ {"role": "user", "content": "Hello!"} ] ) print(f"Model: {response.model}") print(f"Stop Reason: {response.stop_reason}") print(f"Input Tokens: {response.usage.input_tokens}") print(f"Output Tokens: {response.usage.output_tokens}") print(f"\nContent: {response.content[0].text}") if __name__ == "__main__": main() ``` ## Async Messages API ```python import asyncio from apertis import AsyncApertis async def main(): client = AsyncApertis() response = await client.messages.create( model="claude-sonnet-4-6", max_tokens=1024, messages=[ {"role": "user", "content": "What is the capital of France?"} ] ) print(response.content[0].text) if __name__ == "__main__": asyncio.run(main()) ``` ## Context Compression Reduce token usage for long conversations by enabling [context compression](/api/text-generation/context-compression): ```python from apertis import Apertis def main(): client = Apertis() message = client.messages.create( model="claude-sonnet-4-6", max_tokens=1024, messages=[ {"role": "user", "content": "Explain distributed systems"}, {"role": "assistant", "content": "Distributed systems are..."}, # ... many turns of conversation history ... {"role": "user", "content": "Summarize the key points"} ], compression={ "enabled": True, "strategy": "on", "model": "gpt-5.4-mini" } ) print(message.content[0].text) if __name__ == "__main__": main() ``` ## Supported Models The Messages API supports all Claude models: | Model | Description | |-------|-------------| | `claude-opus-4-5-20251101` | Most capable, best for complex tasks | | `claude-sonnet-4-6` | Balanced performance and cost | | `claude-haiku-4.5` | Fastest, most cost-effective | [View all models →](/api/utilities/models) ## API Reference ### Request Parameters | Parameter | Type | Description | |-----------|------|-------------| | `model` | `str` | Model identifier (required) | | `messages` | `list` | Conversation messages (required) | | `max_tokens` | `int` | Maximum tokens to generate (required) | | `system` | `str` | System prompt | | `temperature` | `float` | Sampling temperature (0.0 - 1.0) | | `top_p` | `float` | Nucleus sampling parameter | | `top_k` | `int` | Top-k sampling parameter | | `stop_sequences` | `list` | Sequences that stop generation | | `tools` | `list` | Tool definitions for function calling | | `tool_choice` | `dict` | Tool selection | | `metadata` | `dict` | Request metadata | | `compression` | `dict` | [Context compression](/api/text-generation/context-compression) settings | | `stream` | `bool` | Return an iterator of stream events (0.4.0 or later) | | `thinking` | `dict` | Extended thinking, for example `{"type": "enabled", "budget_tokens": 10000}` (0.4.0 or later) | | `extra_body` | `dict` | Extra fields merged into the top level of the request (0.4.0 or later) | ### Response Object | Field | Type | Description | |-------|------|-------------| | `id` | `str` | Unique message ID | | `type` | `str` | Always `"message"` | | `role` | `str` | Always `"assistant"` | | `content` | `list` | Content blocks (text, tool_use, thinking) | | `model` | `str` | Model used | | `stop_reason` | `str` | Why generation stopped | | `usage` | `object` | Token usage information | --- Source: https://docs.apertis.ai/api/sdks/python-sdk/responses-api # Responses API Use OpenAI's Responses API format for enhanced capabilities including built-in tools, web search, and file handling. ## Prerequisites ```bash pip install "apertis>=0.4.1" openai ``` `apertis` 0.4.0 or later parses Responses API output, as the first example shows. 0.4.1 adds streaming and typed input parts (`input_text`, `input_image`, `input_file`), shown under Streaming and Image Input. The SDK takes no `extra_body`, so the other examples on this page use the OpenAI Python SDK pointed at Apertis. Get your API Key from [**Apertis**](https://apertis.ai/setting?tab=keys) ## Basic Usage ```python from apertis import Apertis def main(): client = Apertis() response = client.responses.create( model="gpt-5.5", input="Explain the concept of machine learning." ) print(response.output_text) # output also holds reasoning and function_call items for item in response.output: print(item.type) if __name__ == "__main__": main() ``` ## Multi-Turn with Input Items ```python import os from openai import OpenAI def main(): client = OpenAI(api_key=os.environ["APERTIS_API_KEY"], base_url="https://api.apertis.ai/v1") response = client.responses.create( model="gpt-5.5", input=[ {"type": "message", "role": "user", "content": "What is Python?"}, {"type": "message", "role": "assistant", "content": "Python is a high-level programming language known for its simplicity and readability."}, {"type": "message", "role": "user", "content": "What are its main uses?"} ] ) print(response.output_text) if __name__ == "__main__": main() ``` ## System Instructions ```python import os from openai import OpenAI def main(): client = OpenAI(api_key=os.environ["APERTIS_API_KEY"], base_url="https://api.apertis.ai/v1") response = client.responses.create( model="gpt-5.5", instructions="You are a helpful coding tutor. Explain concepts clearly with examples.", input="How do I handle exceptions in Python?" ) print(response.output_text) if __name__ == "__main__": main() ``` ## Built-in Web Search ```python import os from openai import OpenAI def main(): client = OpenAI(api_key=os.environ["APERTIS_API_KEY"], base_url="https://api.apertis.ai/v1") response = client.responses.create( model="gpt-5.5", input="What are the latest news about AI regulations?", tools=[{"type": "web_search"}] ) print(response.output_text) # Access search results if available for item in response.output: if item.type == "web_search_call": print(f"\nSearch query: {getattr(item.action, 'query', None)}") if __name__ == "__main__": main() ``` ## Code Interpreter ```python import os from openai import OpenAI def main(): client = OpenAI(api_key=os.environ["APERTIS_API_KEY"], base_url="https://api.apertis.ai/v1") response = client.responses.create( model="gpt-5.5", input="Calculate the compound interest on $10,000 at 5% annual rate for 10 years, compounded monthly. Show the calculation.", tools=[{"type": "code_interpreter"}] ) print(response.output_text) if __name__ == "__main__": main() ``` ## Function Calling ```python import json import os from openai import OpenAI def main(): client = OpenAI(api_key=os.environ["APERTIS_API_KEY"], base_url="https://api.apertis.ai/v1") tools = [ { "type": "function", "function": { "name": "get_weather", "description": "Get current weather for a location", "parameters": { "type": "object", "properties": { "location": {"type": "string", "description": "City name"}, "unit": {"type": "string", "enum": ["celsius", "fahrenheit"]} }, "required": ["location"] } } } ] response = client.responses.create( model="gpt-5.5", input="What's the weather in London?", tools=tools ) for item in response.output: if item.type == "function_call": print(f"Function: {item.name}") print(f"Arguments: {json.dumps(json.loads(item.arguments), indent=2)}") elif item.type == "message": print(f"Message: {item.content}") if __name__ == "__main__": main() ``` ## Streaming `stream=True` returns an iterator of `ResponseStreamEvent` objects (`apertis` 0.4.1 or later). Each event keeps its Responses API `type` and fields. An `error` or `response.failed` event raises the matching `APIError`. ```python from apertis import Apertis def main(): client = Apertis() stream = client.responses.create( model="gpt-5.5", input="Write a short story about a robot learning to paint.", stream=True ) for event in stream: if event.type == "response.output_text.delta": print(event.delta, end="", flush=True) print() if __name__ == "__main__": main() ``` ## Image Input `input_image` takes an `image_url` or a `file_id`; `input_file` takes a `file_id`, `file_url` or `file_data` (`apertis` 0.4.1 or later). ```python from apertis import Apertis def main(): client = Apertis() response = client.responses.create( model="gpt-5.5", input=[ { "type": "message", "role": "user", "content": [ {"type": "input_text", "text": "What's in this image?"}, { "type": "input_image", "image_url": "https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg" } ] } ] ) print(response.output_text) if __name__ == "__main__": main() ``` ## Response with Reasoning ```python import os from openai import OpenAI def main(): client = OpenAI(api_key=os.environ["APERTIS_API_KEY"], base_url="https://api.apertis.ai/v1") response = client.responses.create( model="gpt-5.5", input="Solve: If 3x + 7 = 22, what is x? Show your reasoning.", reasoning={"effort": "high"} ) # Access reasoning if available for item in response.output: if item.type == "reasoning": print("=== Reasoning ===") print(item.content) elif item.type == "message": print("\n=== Answer ===") print(item.content) if __name__ == "__main__": main() ``` ## Response Metadata ```python import os from openai import OpenAI def main(): client = OpenAI(api_key=os.environ["APERTIS_API_KEY"], base_url="https://api.apertis.ai/v1") response = client.responses.create( model="gpt-5.5", input="Hello, world!" ) print(f"Response ID: {response.id}") print(f"Model: {response.model}") print(f"Status: {response.status}") print(f"Input Tokens: {response.usage.input_tokens}") print(f"Output Tokens: {response.usage.output_tokens}") print(f"\nOutput: {response.output_text}") if __name__ == "__main__": main() ``` ## Async Responses API ```python import asyncio import os from openai import AsyncOpenAI async def main(): client = AsyncOpenAI(api_key=os.environ["APERTIS_API_KEY"], base_url="https://api.apertis.ai/v1") response = await client.responses.create( model="gpt-5.5", input="What is quantum computing?" ) print(response.output_text) if __name__ == "__main__": asyncio.run(main()) ``` ## Context Compression Reduce token usage for long conversations by enabling [context compression](/api/text-generation/context-compression). Apertis compresses older conversation history using a lightweight model before forwarding to the target model. ```python import os from openai import OpenAI def main(): client = OpenAI(api_key=os.environ["APERTIS_API_KEY"], base_url="https://api.apertis.ai/v1") response = client.responses.create( model="o4-mini-high", input=[ {"role": "user", "content": "Explain distributed systems"}, {"role": "assistant", "content": "Distributed systems are..."}, # ... many turns of conversation history ... {"role": "user", "content": "Summarize the key points"} ], extra_body={ "compression": { "enabled": True, "strategy": "on", # "on", "conservative", "aggressive" "model": "gpt-5.4-mini" # or "auto" for automatic selection } } ) print(response.output_text) if __name__ == "__main__": main() ``` ## Supported Models The Responses API supports: | Provider | Models | |----------|--------| | OpenAI | `gpt-5.5`, `gpt-5.4-mini`, `o4-mini-high`, `o3-mini-high` | | Others | Models with Responses API compatibility | [View all models →](/api/utilities/models) ## API Reference ### Request Parameters | Parameter | Type | Description | |-----------|------|-------------| | `model` | `str` | Model identifier (required) | | `input` | `str \| list` | User input or conversation (required) | | `instructions` | `str` | System instructions | | `tools` | `list` | Tool definitions | | `temperature` | `float` | Sampling temperature | | `max_output_tokens` | `int` | Maximum tokens to generate | | `stream` | `bool` | Return an iterator of stream events (`apertis` 0.4.1 or later) | | `reasoning` | `dict` | Reasoning configuration | ### Built-in Tools | Tool | Description | |------|-------------| | `web_search` | Real-time web search | | `code_interpreter` | Execute code and analyze data | | `file_search` | Search through uploaded files | ### Response Object | Field | Type | Description | |-------|------|-------------| | `id` | `str` | Response ID | | `model` | `str` | Model used | | `status` | `str` | Response status | | `output` | `list` | Output items | | `output_text` | `str` | Concatenated text output | | `usage` | `object` | Token usage | --- Source: https://docs.apertis.ai/api/sdks/python-sdk/rerank # Rerank > **Caution: Not currently available** > > The SDK has a `client.rerank.create()` method, but it calls `POST /v1/rerank`, which `api.apertis.ai` does not serve: every call raises `NotFoundError` (HTTP 404, checked 2026-10-06). Since `apertis` 0.4.0 the method also emits a `DeprecationWarning` saying so. To order documents by relevance in a RAG pipeline, compare vectors from [`client.embeddings.create()`](/api/sdks/python-sdk/embeddings) instead. --- Source: https://docs.apertis.ai/api/sdks/python-sdk/async # Async Patterns Use asynchronous programming for high-performance applications, concurrent requests, and efficient I/O handling. ## Prerequisites ```bash pip install apertis ``` Get your API Key from [**Apertis**](https://apertis.ai/setting?tab=keys) ## Basic Async Client ```python import asyncio from apertis import AsyncApertis async def main(): client = AsyncApertis() response = await client.chat.completions.create( model="gpt-5.4-mini", messages=[ {"role": "user", "content": "Hello, async world!"} ] ) print(response.choices[0].message.content) if __name__ == "__main__": asyncio.run(main()) ``` ## Concurrent Requests ```python import asyncio from apertis import AsyncApertis async def ask_question(client: AsyncApertis, question: str) -> str: """Send a single question and return the response.""" response = await client.chat.completions.create( model="gpt-5.4-mini", messages=[ {"role": "user", "content": question} ] ) return response.choices[0].message.content async def main(): client = AsyncApertis() questions = [ "What is Python?", "What is JavaScript?", "What is Rust?", "What is Go?", ] # Run all requests concurrently tasks = [ask_question(client, q) for q in questions] answers = await asyncio.gather(*tasks) for question, answer in zip(questions, answers): print(f"Q: {question}") print(f"A: {answer[:100]}...\n") if __name__ == "__main__": asyncio.run(main()) ``` ## Async Streaming ```python import asyncio from apertis import AsyncApertis async def main(): client = AsyncApertis() stream = await client.chat.completions.create( model="gpt-5.4-mini", messages=[ {"role": "user", "content": "Write a haiku about programming."} ], stream=True ) async for chunk in stream: if chunk.choices[0].delta.content: print(chunk.choices[0].delta.content, end="", flush=True) print() if __name__ == "__main__": asyncio.run(main()) ``` ## Semaphore for Rate Limiting ```python import asyncio from apertis import AsyncApertis async def process_item( client: AsyncApertis, semaphore: asyncio.Semaphore, item: str ) -> dict: """Process a single item with rate limiting.""" async with semaphore: response = await client.chat.completions.create( model="gpt-5.4-mini", messages=[ {"role": "user", "content": f"Summarize: {item}"} ] ) return { "item": item, "summary": response.choices[0].message.content } async def main(): client = AsyncApertis() items = [f"Topic {i}: Some content to summarize." for i in range(20)] # Limit to 5 concurrent requests semaphore = asyncio.Semaphore(5) tasks = [process_item(client, semaphore, item) for item in items] results = await asyncio.gather(*tasks) for result in results[:3]: print(f"{result['item'][:30]}... -> {result['summary'][:50]}...") print(f"\nProcessed {len(results)} items") if __name__ == "__main__": asyncio.run(main()) ``` ## Async Context Manager ```python import asyncio from apertis import AsyncApertis async def main(): async with AsyncApertis() as client: response = await client.chat.completions.create( model="gpt-5.4-mini", messages=[ {"role": "user", "content": "Hello!"} ] ) print(response.choices[0].message.content) # Client is automatically closed after the context if __name__ == "__main__": asyncio.run(main()) ``` ## Async Batch Embeddings ```python import asyncio from apertis import AsyncApertis async def embed_batch( client: AsyncApertis, texts: list[str], batch_size: int = 20 ) -> list[list[float]]: """Embed texts in batches.""" all_embeddings = [] for i in range(0, len(texts), batch_size): batch = texts[i:i + batch_size] response = await client.embeddings.create( model="jina-embeddings-v3", input=batch ) all_embeddings.extend([data.embedding for data in response.data]) print(f"Processed {min(i + batch_size, len(texts))}/{len(texts)}") return all_embeddings async def main(): client = AsyncApertis() texts = [f"Document number {i} with some content." for i in range(100)] embeddings = await embed_batch(client, texts) print(f"\nGenerated {len(embeddings)} embeddings") print(f"Embedding dimensions: {len(embeddings[0])}") if __name__ == "__main__": asyncio.run(main()) ``` ## Error Handling in Async ```python import asyncio from apertis import AsyncApertis from apertis import APIError, RateLimitError, APIConnectionError async def safe_request(client: AsyncApertis, prompt: str) -> str | None: """Make a request with error handling.""" try: response = await client.chat.completions.create( model="gpt-5.4-mini", messages=[ {"role": "user", "content": prompt} ] ) return response.choices[0].message.content except RateLimitError as e: print(f"Rate limited: {e}. Waiting before retry...") await asyncio.sleep(60) return None except APIConnectionError as e: print(f"Connection error: {e}") return None except APIError as e: print(f"API error: {e}") return None async def main(): client = AsyncApertis() prompts = ["Hello!", "How are you?", "What's 2+2?"] tasks = [safe_request(client, p) for p in prompts] results = await asyncio.gather(*tasks, return_exceptions=True) for prompt, result in zip(prompts, results): if isinstance(result, Exception): print(f"Error for '{prompt}': {result}") elif result: print(f"'{prompt}' -> {result[:50]}...") if __name__ == "__main__": asyncio.run(main()) ``` ## Async Retry with Backoff ```python import asyncio import random from apertis import AsyncApertis from apertis import APIError, RateLimitError async def request_with_retry( client: AsyncApertis, prompt: str, max_retries: int = 3, base_delay: float = 1.0 ) -> str: """Make a request with exponential backoff retry.""" for attempt in range(max_retries): try: response = await client.chat.completions.create( model="gpt-5.4-mini", messages=[ {"role": "user", "content": prompt} ] ) return response.choices[0].message.content except (RateLimitError, APIError) as e: if attempt == max_retries - 1: raise # Exponential backoff with jitter delay = base_delay * (2 ** attempt) + random.uniform(0, 1) print(f"Attempt {attempt + 1} failed: {e}. Retrying in {delay:.1f}s...") await asyncio.sleep(delay) raise RuntimeError("Max retries exceeded") async def main(): client = AsyncApertis() try: result = await request_with_retry(client, "Hello!") print(f"Result: {result}") except Exception as e: print(f"Failed after retries: {e}") if __name__ == "__main__": asyncio.run(main()) ``` ## Async Producer-Consumer Pattern ```python import asyncio from apertis import AsyncApertis async def producer(queue: asyncio.Queue, items: list[str]): """Add items to the queue.""" for item in items: await queue.put(item) # Signal completion await queue.put(None) async def consumer( client: AsyncApertis, queue: asyncio.Queue, results: list ): """Process items from the queue.""" while True: item = await queue.get() if item is None: queue.task_done() break response = await client.chat.completions.create( model="gpt-5.4-mini", messages=[ {"role": "user", "content": f"Process: {item}"} ] ) results.append({ "input": item, "output": response.choices[0].message.content }) queue.task_done() async def main(): client = AsyncApertis() queue = asyncio.Queue() results = [] items = [f"Item {i}" for i in range(10)] # Start producer and multiple consumers producer_task = asyncio.create_task(producer(queue, items)) consumer_tasks = [ asyncio.create_task(consumer(client, queue, results)) for _ in range(3) # 3 concurrent consumers ] # Wait for producer await producer_task # Signal consumers to stop for _ in consumer_tasks: await queue.put(None) # Wait for consumers await asyncio.gather(*consumer_tasks) print(f"Processed {len(results)} items") for r in results[:3]: print(f" {r['input']} -> {r['output'][:30]}...") if __name__ == "__main__": asyncio.run(main()) ``` ## API Reference ### AsyncApertis Client ```python from apertis import AsyncApertis # With environment variable client = AsyncApertis() # With explicit API key client = AsyncApertis(api_key="sk-your-api-key") # As context manager async with AsyncApertis() as client: # Use client pass ``` ### Available Async Methods | Endpoint | Method | |----------|--------| | Chat Completions | `await client.chat.completions.create()` | | Embeddings | `await client.embeddings.create()` | | Messages | `await client.messages.create()` | | Responses | `await client.responses.create()` (0.4.0 or later) | | Audio | `await client.audio.speech.create()`, `await client.audio.transcriptions.create()`, `await client.audio.translations.create()` (0.4.0 or later) | | Rerank | `await client.rerank.create()` (endpoint not currently available) | ## Best Practices 1. **Use semaphores** - Limit concurrent requests to avoid rate limits 2. **Handle errors gracefully** - Implement retry logic with backoff 3. **Use context managers** - Ensure resources are properly cleaned up 4. **Batch when possible** - Group related operations for efficiency 5. **Consider timeouts** - Set appropriate timeouts for operations 6. **Profile performance** - Measure actual improvements from async --- Source: https://docs.apertis.ai/api/sdks/langchain # LangChain ### Installing LangChain ```bash pip install langchain pip install langchain-openai ``` ### Apertis Usage Example Please obtain your API Key from [**Apertis Key**](https://apertis.ai/setting?tab=keys) ```python from langchain_openai import ChatOpenAI from langchain_core.messages import HumanMessage, SystemMessage CONFIG = { "api_key": "APERTIS_API_KEY", "base_url": "https://api.apertis.ai/v1", "model": "gpt-5.4-mini", "temperature": 0.7, "request_timeout": 30, } def get_llm(**kwargs): config = CONFIG.copy() config.update(kwargs) return ChatOpenAI(**config) def ask(message, **kwargs): llm = get_llm(**kwargs) try: response = llm.invoke(message) return response.content except: response = "" for chunk in llm.stream(message): response += chunk.content return response def ask_stream(message, **kwargs): llm = get_llm(**kwargs) for chunk in llm.stream(message): print(chunk.content, end="", flush=True) print() if __name__ == "__main__": response = ask("Hi, introduce yourself") print(f"Response: {response}\n") messages = [ SystemMessage(content="You are Python expert"), HumanMessage(content="What is LangChain?") ] response = ask(messages) print(f"Expert Response: {response}\n") creative_response = ask("Write a poem", temperature=0.9) print(f"Response: {creative_response}\n") print("Streaming: ", end="") ask_stream("Explain AI") # Switch Model fast_response = ask("1+1=?", model="grok-4-fast") print(f"\nResponse: {fast_response}") ``` ## Context Compression Enable [context compression](/api/text-generation/context-compression) via extra headers to reduce token usage for long conversations: ```python from langchain_openai import ChatOpenAI llm = ChatOpenAI( model="gpt-5.5", api_key="APERTIS_API_KEY", base_url="https://api.apertis.ai/v1", default_headers={ "X-Context-Compression": "on", "X-Compression-Model": "gpt-5.4-mini", }, ) response = llm.invoke(long_conversation_messages) ``` --- Source: https://docs.apertis.ai/api/sdks/llamaindex # LlamaIndex LlamaIndex has native support for Apertis through the `llama-index-llms-apertis` package. ## Installation ```bash pip install llama-index-llms-apertis llama-index ``` ## Basic Setup ```python from llama_index.llms.apertis import Apertis from llama_index.core.llms import ChatMessage # Configure with your API key from https://apertis.ai/setting?tab=keys llm = Apertis( api_key="sk-your-api-key", model="gpt-5.2", max_tokens=256, context_window=4096, ) ``` ## Chat API ```python message = ChatMessage(role="user", content="Tell me a joke") resp = llm.chat([message]) print(resp) ``` ### Streaming Chat ```python message = ChatMessage(role="user", content="Tell me a story in 250 words") resp = llm.stream_chat([message]) for r in resp: print(r.delta, end="") ``` ## Completion API ```python resp = llm.complete("Tell me a joke") print(resp) ``` ### Streaming Completion ```python resp = llm.stream_complete("Tell me a story in 250 words") for r in resp: print(r.delta, end="") ``` ## Using Different Models Use any compatible model returned by the Apertis model catalog: ```python # OpenAI GPT llm = Apertis(model="gpt-5.2") # Anthropic Claude llm = Apertis(model="claude-sonnet-4-6") # Google Gemini llm = Apertis(model="gemini-3-flash-preview") ``` ## Context Compression Enable [context compression](/api/text-generation/context-compression) via extra headers to reduce token usage for long conversations: ```python from llama_index.llms.openai_like import OpenAILike llm = OpenAILike( model="gpt-5.5", api_key="APERTIS_API_KEY", api_base="https://api.apertis.ai/v1", additional_kwargs={ "extra_headers": { "X-Context-Compression": "on", "X-Compression-Model": "gpt-5.4-mini", } }, ) response = llm.complete("Summarize the key points from our discussion") ``` ## Supported Models | Provider | Model ID | |----------|----------| | OpenAI | `gpt-5.2`, `gpt-5.4-mini` | | Anthropic | `claude-sonnet-4-6`, `claude-haiku-4.5` | | Google | `gemini-3.1-pro-preview`, `gemini-3-flash-preview` | For the full list of models, visit [Apertis Models](https://apertis.ai/models). --- Source: https://docs.apertis.ai/api/sdks/litellm # LiteLLM [LiteLLM](https://github.com/BerriAI/litellm) provides a unified interface to call 100+ LLMs. Apertis is supported as a native provider. ## Installation ```bash pip install litellm ``` ## Environment Setup Set your Apertis API key: ```bash export APERTIS_API_KEY="sk-your-api-key" ``` Or in Python: ```python import os os.environ["APERTIS_API_KEY"] = "sk-your-api-key" ``` Get your API key from [Settings → API Keys](https://apertis.ai/setting?tab=keys). ## Basic Usage ### Completion ```python import os from litellm import completion os.environ["APERTIS_API_KEY"] = "sk-your-api-key" messages = [{"role": "user", "content": "What is the capital of France?"}] response = completion( model="apertis/gpt-5.2", messages=messages ) print(response.choices[0].message.content) ``` ### Streaming ```python from litellm import completion messages = [{"role": "user", "content": "Write a short poem about AI"}] response = completion( model="apertis/gpt-5.2", messages=messages, stream=True ) for chunk in response: if chunk.choices[0].delta.content: print(chunk.choices[0].delta.content, end="") ``` ## Using Different Models Use compatible models returned by the Apertis model catalog through the `apertis/` prefix: ```python # OpenAI GPT response = completion(model="apertis/gpt-5.2", messages=messages) # Anthropic Claude response = completion(model="apertis/claude-sonnet-4-6", messages=messages) # Google Gemini response = completion(model="apertis/gemini-3-flash-preview", messages=messages) ``` ## LiteLLM Proxy Configuration ### 1. Export API Key ```bash export APERTIS_API_KEY="sk-your-api-key" ``` ### 2. Configure config.yaml ```yaml model_list: - model_name: gpt-5.2 litellm_params: model: apertis/gpt-5.2 api_key: os.environ/APERTIS_API_KEY - model_name: claude-sonnet litellm_params: model: apertis/claude-sonnet-4-6 api_key: os.environ/APERTIS_API_KEY - model_name: gemini-flash litellm_params: model: apertis/gemini-3-flash-preview api_key: os.environ/APERTIS_API_KEY ``` ### 3. Start Proxy ```bash litellm --config config.yaml ``` ## Context Compression Enable [context compression](/api/text-generation/context-compression) via extra headers to reduce token usage for long conversations: ```python from litellm import completion response = completion( model="apertis/gpt-5.5", messages=[ {"role": "user", "content": "Explain distributed systems"}, {"role": "assistant", "content": "Distributed systems are..."}, # ... long conversation history ... {"role": "user", "content": "Summarize the key points"}, ], extra_headers={ "X-Context-Compression": "on", "X-Compression-Model": "gpt-5.4-mini", }, ) ``` ## Supported Parameters All standard OpenAI-compatible parameters are supported: | Parameter | Description | |-----------|-------------| | `messages` | Chat messages array | | `model` | Model ID with `apertis/` prefix | | `stream` | Enable streaming responses | | `temperature` | Sampling temperature (0-2) | | `top_p` | Nucleus sampling | | `max_tokens` | Maximum response tokens | | `frequency_penalty` | Frequency penalty (-2 to 2) | | `presence_penalty` | Presence penalty (-2 to 2) | | `stop` | Stop sequences | | `tools` | Function/tool definitions | | `tool_choice` | Tool selection mode | ## Popular Models | Provider | Model ID | |----------|----------| | OpenAI | `apertis/gpt-5.2`, `apertis/gpt-5.4-mini` | | Anthropic | `apertis/claude-sonnet-4-6`, `apertis/claude-haiku-4.5` | | Google | `apertis/gemini-3.1-pro-preview`, `apertis/gemini-3-flash-preview` | For the full list of models, visit [Apertis Models](https://apertis.ai/models). --- Source: https://docs.apertis.ai/api/sdks/mcp-server # Apertis MCP Server `@apertis/mcp-server` is an [MCP](https://modelcontextprotocol.io) server that lets agents (Claude Code, OpenCode, or any MCP-compatible client) manage your Apertis account and query the API gateway at runtime — list models, check quota, create API keys, ask for task-aware model recommendations, and delegate bulk grunt work to a cheap coworker model. GitHub: [apertis-ai/apertis-mcp](https://github.com/apertis-ai/apertis-mcp) · npm: `@apertis/mcp-server` ## Installation No install step required — the server runs via `npx`. Just point your MCP client at it. ### Claude Code Add to `~/.claude/settings.json`: ```json { "mcpServers": { "apertis": { "command": "npx", "args": ["@apertis/mcp-server"], "env": { "APERTIS_API_KEY": "sk-your-key-here" } } } } ``` ### OpenCode Add to `openclaw.json`: ```json { "mcpServers": { "apertis": { "command": "npx", "args": ["@apertis/mcp-server"], "env": { "APERTIS_API_KEY": "sk-your-key-here" } } } } ``` Restart your agent and the Apertis tools become available. ## Environment Variables | Variable | Required | Default | Description | |----------|----------|---------|-------------| | `APERTIS_API_KEY` | Yes | — | Your Apertis API key (`sk-...`) | | `APERTIS_BASE_URL` | No | `https://api.apertis.ai` | API base URL | | `APERTIS_COWORKER_MODEL` | No | `deepseek-v4-flash` | Intern model used by the `delegate` tool | ## Available Tools | Tool | Description | |------|-------------| | `list_models` | List available models with optional filtering (free/paid, capability) | | `get_model_info` | Get detailed info about a specific model (pricing, context, provider) | | `compare_models` | Side-by-side comparison of 2–5 models | | `check_quota` | Check account balance, subscription status, remaining quota | | `get_usage_stats` | Usage statistics by model and period (today/week/month) | | `list_api_keys` | List API keys with status and quota (masked) | | `create_api_key` | Create a new API key with optional quota limit | | `suggest_model` | Freeform keyword-based model search over the full catalog | | `recommend_model` | Curated Apertis pick for a task type with live pricing | | `delegate` | Offload bulk grunt work (file reads, boilerplate, summarization) to a cheap coworker model — keeps your Claude usage limit intact | ## `recommend_model` — Dynamic Model Selection Prefer `recommend_model` when the task fits one of the 5 canonical types — it wraps the [`GET /v1/recommend`](/api/utilities/recommend) endpoint and returns a single opinionated pick rather than a ranked list. **Input** | Parameter | Required | Description | |-----------|----------|-------------| | `task` | Yes | `coding` / `long-context` / `fast-chat` / `reasoning` / `vision` | | `budget` | No | `low` / `medium` (default) / `high` | **Example conversation** ``` You: What model should I use for coding right now? Agent: [calls recommend_model(task="coding", budget="medium")] → Apertis recommends claude-sonnet-4-6 Input: $2.40/1M, Output: $12.00/1M Why: Best coding ability per dollar. 200K context. Alternatives: - deepseek-v3 ($0.30/1M) — 3x cheaper, good for simpler coding - claude-opus-4-6 ($4.00/1M) — most capable, higher cost ``` Use the returned `model` value directly in subsequent API calls. ## `delegate` — Offload Grunt Work to a Cheap Coworker `delegate` gives your agent a cheap "intern". Claude stays the manager on your Anthropic subscription and hands off high-volume, low-judgement work — bulk file reads, boilerplate generation, summarization — to a cheap model through the Apertis gateway. The coworker reads files itself, so their bulk content **never enters your agent's context**, and the work runs on Apertis credit instead of your Claude weekly/5-hour limit. **Input** | Parameter | Required | Description | |-----------|----------|-------------| | `instruction` | Yes | What the intern should do | | `file_paths` | No | Local file paths the coworker reads itself; content never enters your context | | `content` | No | Inline content to process | | `model` | No | Override the intern model (default `deepseek-v4-flash`) | The coworker calls are ordinary Apertis requests billed against your API key — one credit pool, transparent per-call usage in your dashboard. Unreadable file paths and gateway errors are surfaced as tool errors, never silently swallowed. **Example conversation** ``` You: Summarize the errors in build.log Agent: [calls delegate(instruction="Summarize the errors in <=10 bullets", file_paths=["build.log"])] → returns a concise summary; the full log never enters Claude's context ``` **Auto-delegation.** To make your agent delegate without being asked each time, paste the routing rules from [`coworker-rules.md`](https://github.com/apertis-ai/apertis-mcp/blob/main/coworker-rules.md) into your project's `CLAUDE.md`. They tell the agent when to delegate (bulk reads, boilerplate, summarization) versus do the work itself. ## Resources In addition to tools, the server exposes three read-only resources: | Resource URI | Description | |--------------|-------------| | `apertis://account` | Current account info, balance, subscription | | `apertis://models` | Full model catalog (cached 5 min) | | `apertis://usage/today` | Today's usage summary | ## Security - All API keys in tool output are masked (first 4 + last 4 characters) - New keys are shown in full **once** — save them immediately - Keep `APERTIS_API_KEY` out of source control; use your MCP client's env-var expansion ## Docs MCP server (this documentation) The docs MCP server is a different server from `@apertis/mcp-server` above. It needs no API key and never sees your account: it lets an agent search and read these documentation pages, so its answers follow the current docs rather than its training data. | | | |---|---| | Endpoint | `https://docs.apertis.ai/mcp` (MCP Streamable HTTP, stateless, no key) | | `search_docs` | `query`, optional `limit` (1–10): ranked pages with title, URL and a text snippet | | `get_page` | `url` (a docs.apertis.ai URL or path, as `search_docs` returns it): the page as Markdown | **Claude Code** ```bash claude mcp add --transport http apertis-docs https://docs.apertis.ai/mcp ``` **Cursor**: add to `~/.cursor/mcp.json`, or `.cursor/mcp.json` in a project: ```json { "mcpServers": { "apertis-docs": { "url": "https://docs.apertis.ai/mcp" } } } ``` **VS Code**: add to `.vscode/mcp.json`: ```json { "servers": { "apertis-docs": { "type": "http", "url": "https://docs.apertis.ai/mcp" } } } ``` Any other client that supports remote (Streamable HTTP) MCP servers connects to the same URL. The same pages are also plain files: [`/llms.txt`](/llms.txt) lists every page with a one-line description and a link to its Markdown, and [`/llms-full.txt`](/llms-full.txt) holds all of them in one file. --- Source: https://docs.apertis.ai/api/sdks/cli # @apertis/cli [`@apertis/cli`](https://www.npmjs.com/package/@apertis/cli) is a small terminal tool for the Apertis API gateway. See your usage and spend, inspect the live model catalog, and **benchmark latency, throughput, and real measured cost** across models — straight from your terminal. GitHub: [apertis-ai/apertis-cli](https://github.com/apertis-ai/apertis-cli) · npm: `@apertis/cli` ## Installation ```bash npm install -g @apertis/cli ``` Or run it without installing: ```bash npx @apertis/cli usage ``` Requires Node.js 18+. ## Authentication Get an API key from the [Apertis Dashboard](https://apertis.ai/setting?tab=keys), then provide it any of these ways (highest precedence first): ```bash apertis --key sk-... usage # 1. per-command flag export APERTIS_API_KEY=sk-... # 2. environment variable apertis config set-key sk-... # 3. saved to ~/.apertis/config.json (chmod 0600) ``` > **Warning: Security** > > A key passed as a command argument (`--key`, `config set-key`) can be captured by shell history and process listings. On sensitive or shared machines, prefer `APERTIS_API_KEY` or the saved config file. The CLI never prints your full key — it is masked in all output and redacted from error messages. ## Commands ### `apertis usage` Your API key's lifetime usage and spend. ```bash apertis usage apertis usage --json ``` ### `apertis models` List available models, with optional filters. ```bash apertis models apertis models --owner openai apertis models --search claude apertis models --json ``` ### `apertis benchmark ` Benchmark one or more models head-to-head. Reports client-measured latency (avg / p50), Apertis's **server-side** latency, throughput (tokens/sec), and the **actual measured cost** of the run. ```bash apertis benchmark gpt-5.4-mini claude-sonnet-4-6 apertis benchmark gpt-5.4-mini -n 5 --prompt "Summarize the theory of relativity." apertis benchmark gpt-5.4-mini --json ``` Cost is measured from your key's `used_quota_usd` delta over the run — so it reflects real spend rather than a pricing-table estimate. (Best-effort: concurrent use of the same key during a benchmark can inflate it.) ### `apertis config` ```bash apertis config set-key sk-... # save key (and optionally --base-url) apertis config show # show saved config (key masked) ``` ## Global flags | Flag | Description | |------|-------------| | `-k, --key ` | API key (overrides env and config file) | | `--base-url ` | API base URL (default `https://api.apertis.ai/v1`) | | `--json` | Machine-readable JSON output | ## Links - npm: [`@apertis/cli`](https://www.npmjs.com/package/@apertis/cli) - Source: [github.com/apertis-ai/apertis-cli](https://github.com/apertis-ai/apertis-cli) - Model list & pricing: [apertis.ai/pricing](https://apertis.ai/pricing) --- Source: https://docs.apertis.ai/api/utilities/models # Models ```json /v1/models ``` ## List all models Returns available models in OpenAI-compatible format. The response depends on your API key type: - **Standard API key** (`sk-`): Returns all publicly available models - **Subscription API key** (`sk-sub-`): Returns only models included in your subscription plan ### HTTP Request ```bash tab="cURL" curl https://api.apertis.ai/v1/models \ -H "Content-Type: application/json" \ -H "Authorization: Bearer " ``` ```python tab="Python" from openai import OpenAI client = OpenAI( api_key="", base_url="https://api.apertis.ai/v1" ) for model in client.models.list(): print(model.id) ``` ```javascript tab="JavaScript" import OpenAI from 'openai'; const client = new OpenAI({ apiKey: '', baseURL: 'https://api.apertis.ai/v1' }); for await (const model of client.models.list()) { console.log(model.id); } ``` - ``: Your API key ### Response (Standard Key) ```json { "object": "list", "data": [ { "id": "gpt-5.5", "object": "model", "created": 1626777600, "owned_by": "OpenAI", "root": "gpt-5.5", "parent": null } ] } ``` ### Response (Subscription Key) When using a subscription API key, the response includes additional fields to help you understand quota costs: ```json { "object": "list", "data": [ { "id": "claude-sonnet-4-6", "object": "model", "created": 1626777600, "owned_by": "Anthropic", "root": "claude-sonnet-4-6", "parent": null, "multiplier": 2.1, "tier": "pro", "context_length": 1000000 }, { "id": "gemini-2.5-flash-preview", "object": "model", "created": 1626777600, "owned_by": "Google", "root": "gemini-2.5-flash-preview", "parent": null, "multiplier": 0, "tier": "free" } ] } ``` | Field | Description | |-------|-------------| | `multiplier` | Quota cost multiplier. `0` means the model is free and does not consume quota | | `tier` | The plan tier where this model originates (`free`, `lite`, `pro`, `max`) | | `context_length` | Maximum context window size in tokens (when available) | > **Tip** > > These extra fields are ignored by standard OpenAI SDKs, so your existing code works without changes. ### Plan Model Access Models are organized by tier with inheritance — higher-tier plans include all models from lower tiers: | Plan | Accessible Models | |------|-------------------| | **Lite** | Lite + Free models | | **Pro** | Pro + Lite + Free models | | **Max** | Max + Pro + Lite + Free models | ## Retrieve a model Returns details for a single model. ### HTTP Request ```bash tab="cURL" curl https://api.apertis.ai/v1/models/gemini-2.5-flash-preview \ -H "Authorization: Bearer " ``` ```python tab="Python" from openai import OpenAI client = OpenAI( api_key="", base_url="https://api.apertis.ai/v1" ) model = client.models.retrieve("gemini-2.5-flash-preview") print(model.id, model.owned_by) ``` ```javascript tab="JavaScript" import OpenAI from 'openai'; const client = new OpenAI({ apiKey: '', baseURL: 'https://api.apertis.ai/v1' }); const model = await client.models.retrieve('gemini-2.5-flash-preview'); console.log(model.id, model.owned_by); ``` ### Response Returns the model object if found. When using a subscription key, only models within your plan are accessible — requesting a model outside your plan returns a `model_not_found` error. ### Python Example ```python from openai import OpenAI client = OpenAI( api_key="sk-sub-your-subscription-key", base_url="https://api.apertis.ai/v1" ) # List models available in your plan models = client.models.list() for model in models: print(model.id) # Retrieve a specific model model = client.models.retrieve("claude-sonnet-4-6") print(model.id, model.owned_by) ``` ### Node.js Example ```javascript import OpenAI from 'openai'; const client = new OpenAI({ apiKey: 'sk-sub-your-subscription-key', baseURL: 'https://api.apertis.ai/v1' }); // List models available in your plan const models = await client.models.list(); for await (const model of models) { console.log(model.id); } ``` --- Source: https://docs.apertis.ai/api/utilities/recommend # Recommend ```json /v1/recommend ``` ## Dynamic model recommendation Returns an opinionated, task-aware model recommendation with live pricing. Use this endpoint when building coding agents or automations that should pick the right model at runtime instead of hard-coding a model ID. The recommendation always reflects current platform pricing and model availability, so the returned `model` can be used directly in subsequent API calls. ### HTTP Request ```bash curl "https://api.apertis.ai/v1/recommend?task=coding&budget=medium" \ -H "Authorization: Bearer " ``` - ``: Your API key ### Query Parameters | Parameter | Required | Description | |-----------|----------|-------------| | `task` | Yes | One of `coding`, `long-context`, `fast-chat`, `reasoning`, `vision` | | `budget` | No | One of `low`, `medium`, `high`. Defaults to `medium` | **Task types** - `coding` — Multi-file edits, debugging, code review, architecture - `long-context` — Large codebases, long documents, multi-file reasoning - `fast-chat` — Customer support, simple Q&A, chat workloads - `reasoning` — Math, logic, structured problem solving - `vision` — Images, diagrams, screenshots, UI understanding **Budget tiers** - `low` — Cheapest valid candidate for the task - `medium` — Editorial pick (first curated candidate); the best balance of quality and cost - `high` — Most capable candidate, regardless of price ### Response ```json { "model": "claude-sonnet-4-6", "task": "coding", "budget": "medium", "input_price_per_1m": 2.40, "output_price_per_1m": 12.00, "reason": "Best coding ability per dollar. 200K context.", "alternatives": [ { "model": "deepseek-v3", "input_price_per_1m": 0.30, "note": "3x cheaper, good for simpler coding tasks" }, { "model": "gpt-5.5", "input_price_per_1m": 4.00, "note": "stronger general reasoning, higher cost" } ] } ``` | Field | Description | |-------|-------------| | `model` | The recommended model ID. Use this directly in `/v1/chat/completions`, `/v1/messages`, etc. | | `task` | Echoes the requested task type | | `budget` | Echoes the applied budget (`medium` if unspecified) | | `input_price_per_1m` | Current input price in USD per 1M tokens | | `output_price_per_1m` | Current output price in USD per 1M tokens | | `reason` | Short rationale for the pick | | `alternatives[]` | Up to 2 other candidates for the same task, with price and a usage note | ### Error responses | Status | Condition | |--------|-----------| | `400` | Missing or invalid `task`, or invalid `budget` value | | `401` | Missing or invalid API key | | `503` | No candidate models available for this task (rare — indicates upstream pricing misconfiguration) | ### Usage example (Python) ```python import os import requests from openai import OpenAI # Step 1: ask Apertis what to use rec = requests.get( "https://api.apertis.ai/v1/recommend", params={"task": "coding", "budget": "medium"}, headers={"Authorization": f"Bearer {os.environ['APERTIS_API_KEY']}"}, ).json() # Step 2: use the returned model ID directly client = OpenAI( base_url="https://api.apertis.ai/v1", api_key=os.environ["APERTIS_API_KEY"], ) response = client.chat.completions.create( model=rec["model"], messages=[{"role": "user", "content": "Refactor this function..."}], ) ``` > **Tip** > > Pair `budget=low` with cost-sensitive background jobs and `budget=high` with user-facing interactive tasks where quality matters most. `medium` is tuned for the default case and mirrors the curated recommendations in the [Apertis Model Picker skill](https://github.com/apertis-ai/apertis-skills). --- Source: https://docs.apertis.ai/api/utilities/fallback-models # Fallback Models Apertis provides a powerful fallback model mechanism that automatically switches to backup models when the primary model request fails, ensuring high availability and service stability. ## Overview The fallback model mechanism automatically handles the following scenarios: - Model service timeouts or unresponsiveness - Network connectivity issues - Specified model temporarily unavailable - Service provider temporary outages When the primary model fails, the system will attempt backup models in your specified priority order until one succeeds. ## Configuration Methods ### Request-Level Configuration (Highest Priority) You can specify fallback model configuration directly in each API request: ```json { "model": "gpt-5.5", "messages": [ {"role": "user", "content": "Hello, how are you?"} ], "fallback_models": ["gpt-5.4-mini", "claude-haiku-4.5"], "fallback_timeout": 25000, "fallback_enabled": true } ``` ### Token-Level Configuration You can also pre-configure fallback models in your API Token settings, which will automatically apply to all requests using that token. ## Parameter Reference ### `fallback_models` - **Type**: Array of strings - **Description**: List of fallback models in priority order - **Limit**: Maximum 5 fallback models - **Example**: `["gpt-5.4-mini", "claude-haiku-4.5", "gemini-2.5-flash"]` ### `fallback_timeout` - **Type**: Integer - **Unit**: Milliseconds (ms) - **Range**: 5,000 - 300,000 milliseconds (5 - 300 seconds) - **Default**: 15,000 milliseconds (15 seconds) - **Description**: Wait time before switching to the next fallback model. Automatically capped by remaining `X-Timeout` budget. ### `fallback_enabled` - **Type**: Boolean - **Default**: false - **Description**: Whether to enable the fallback model mechanism ## Fallback Trigger Conditions The system automatically triggers fallback models in the following situations: ### 1. Timeout Errors - Request exceeds the configured `fallback_timeout` duration - Context deadline exceeded ### 2. Connection Errors - Connection refused - Connection reset - Network unreachable - DNS resolution failures ### 3. Model Errors - Model not found - Invalid model - Model temporarily unavailable ### 4. HTTP Status Errors - Non-200 HTTP status codes ## Request Examples ### Python Examples ```python import openai # Configure Apertis client = openai.OpenAI( api_key="sk-your-api-key", base_url="https://api.apertis.ai/v1" ) # Chat request with fallback models response = client.chat.completions.create( model="gpt-5.5", messages=[ {"role": "user", "content": "Hello, how are you?"} ], # Fallback configuration extra_body={ "fallback_models": ["gpt-5.4-mini", "claude-haiku-4.5"], "fallback_timeout": 25000, # 25 second timeout "fallback_enabled": True } ) print(response.choices[0].message.content) # Check if fallback model was used if hasattr(response, 'headers'): if response.headers.get('X-Fallback-Used') == 'true': print(f"Fallback model used: {response.headers.get('X-Actual-Model')}") print(f"Original model: {response.headers.get('X-Fallback-From')}") print(f"Fallback reason: {response.headers.get('X-Fallback-Reason')}") ``` ### Complete Example with requests Library ```python import requests import json url = "https://api.apertis.ai/v1/chat/completions" headers = { "Authorization": "Bearer sk-your-api-key", "Content-Type": "application/json" } payload = { "model": "gpt-5.5", "messages": [ {"role": "user", "content": "Write a Python function for me"} ], "fallback_models": ["gpt-5.4-mini", "claude-haiku-4.5"], "fallback_timeout": 20000, "fallback_enabled": True } try: response = requests.post(url, headers=headers, json=payload, timeout=60) # Check fallback model usage fallback_used = response.headers.get('X-Fallback-Used', 'false') if fallback_used == 'true': print(f"Fallback model activated!") print(f"Original model: {response.headers.get('X-Fallback-From')}") print(f"Actual model used: {response.headers.get('X-Actual-Model')}") print(f"Fallback reason: {response.headers.get('X-Fallback-Reason')}") result = response.json() print(result['choices'][0]['message']['content']) except requests.exceptions.RequestException as e: print(f"Request failed: {e}") ``` ### cURL Examples ```bash # Basic fallback request curl -X POST "https://api.apertis.ai/v1/chat/completions" \ -H "Authorization: Bearer sk-your-api-key" \ -H "Content-Type: application/json" \ -H "Accept: application/json" \ -d '{ "model": "gpt-5.5", "messages": [ {"role": "user", "content": "Hello, how are you?"} ], "fallback_models": ["gpt-5.4-mini", "claude-haiku-4.5"], "fallback_timeout": 25000, "fallback_enabled": true }' \ -w "\nStatus: %{http_code}\nResponse time: %{time_total}s\n" \ -v ``` ### cURL Example with Header Inspection ```bash # Display full headers to check fallback usage curl -X POST "https://api.apertis.ai/v1/chat/completions" \ -H "Authorization: Bearer sk-your-api-key" \ -H "Content-Type: application/json" \ -D headers.txt \ -d '{ "model": "gpt-5.5", "messages": [ {"role": "user", "content": "Test fallback mechanism"} ], "fallback_models": ["gpt-5.4-mini"], "fallback_timeout": 15000, "fallback_enabled": true }' # Check fallback-related headers echo "Fallback usage information:" grep -i "x-fallback" headers.txt ``` ## Response Headers When using the fallback model mechanism, the API response includes the following custom headers: | Header Name | Description | Example Value | |-------------|-------------|---------------| | `X-Fallback-Used` | Whether fallback model was used | `true` / `false` | | `X-Fallback-From` | Original requested model name | `gpt-5.5` | | `X-Actual-Model` | Actually used model name | `gpt-5.4-mini` | | `X-Fallback-Reason` | Reason for triggering fallback | `primary_model_failed` | ## Timeout Architecture There are two timeout layers that work together: | Layer | Controls | Header / Parameter | Default | |-------|----------|-------------------|---------| | **Total Request Timeout** | Entire request lifecycle (channel retries + model fallback) | `X-Timeout` header | 60s | | **Per-Model Fallback Timeout** | How long to wait on each fallback model before trying the next | `fallback_timeout` body param | 15s | **How they interact:** - `X-Timeout` caps the **total** time. If 40 seconds were spent on channel retries, only 20 seconds remain for fallback models. - `fallback_timeout` controls per-model patience within the remaining budget. If `fallback_timeout` exceeds the remaining total time, it is automatically reduced. - For streaming requests, both timers stop once the upstream starts sending data. ``` X-Timeout: 60000 (total budget) ├── Channel retries: 25s used └── Fallback models: 35s remaining ├── gpt-5.4-mini: fallback_timeout=15s → wait up to 15s └── claude-haiku-4.5: fallback_timeout=15s → wait up to 20s (capped by remaining) ``` ### Subscription Users Subscription plan users can configure the **Fallback Timeout** in the dashboard: **Settings → API Keys → Fallback Timeout** This sets the per-model fallback timeout (5–300 seconds) at the token level, applying to all requests made with that token. Request-level `fallback_timeout` overrides this setting. ## Configuration Priority 1. **Request Level** - `fallback_*` parameters in API request (highest priority) 2. **Token Level** - Fallback model configuration in API Token settings (configurable at **Settings → API Keys**) 3. **System Default** - 15 second per-attempt timeout, fallback disabled ## Best Practices ### 1. Choose Appropriate Fallback Models - Select fast-responding models as backups - Consider cost factors, fallback from expensive to cheaper models - Ensure fallback models support the same features ### 2. Set Reasonable Timeout Values - General recommendation: 20-30 seconds - Complex tasks: 60-120 seconds - Real-time chat: 10-15 seconds ### 3. Fallback Model Count - Recommend 1-3 fallback models - Too many fallback models increase total response time - System limit: maximum 5 fallback models ## Monitoring and Debugging ### Check Fallback Usage ```python # Python example: Check response headers def check_fallback_usage(response): headers = response.headers if hasattr(response, 'headers') else {} fallback_used = headers.get('X-Fallback-Used', 'false') if fallback_used == 'true': print("Fallback model information:") print(f" Original model: {headers.get('X-Fallback-From')}") print(f" Actual model: {headers.get('X-Actual-Model')}") print(f" Fallback reason: {headers.get('X-Fallback-Reason')}") else: print("Using original model, no fallback triggered") ``` ### Common Issues **Q: Why wasn't the fallback model triggered?** A: Check if `fallback_enabled` is set to `true` and `fallback_models` is properly configured. **Q: What happens when all fallback models fail?** A: The system returns the error from the last attempted fallback model. Check model availability and network connectivity. **Q: How to track fallback model usage frequency?** A: Monitor the `X-Fallback-Used` header in responses to track fallback usage statistics. ## Billing Information - Each model is billed at its standard rate - When using fallback models, billing is based on the actually used model - Use the `X-Actual-Model` header to confirm the billing model --- Source: https://docs.apertis.ai/api/utilities/billing-credits # Billing Credits ```json /v1/dashboard/billing/credits ``` ## Check remaining credits Returns your current billing balance, including PAYG credits (in USD) and subscription cycle quota. Authenticate with any API key — no admin key required. > **Tip: Apertis Exclusive** > > Neither OpenAI nor Anthropic offer a programmatic endpoint for checking remaining credits. This is an Apertis-only feature. ### HTTP Request ```bash curl https://api.apertis.ai/v1/dashboard/billing/credits \ -H "Authorization: Bearer " ``` - ``: Any valid API key (`sk-` or `sk-sub-`) ### Response (PAYG Only) When using a standard API key with no active subscription: ```json { "object": "billing_credits", "is_subscriber": false, "payg": { "account_credits": 5.0, "token_used": 2.0, "token_total": 7.0, "token_remaining": 5.0, "token_is_unlimited": false } } ``` ### Response (PAYG with Unlimited Token) When the API key has unlimited quota, token-level fields show `"unlimited"` while `account_credits` still reflects the actual account balance: ```json { "object": "billing_credits", "is_subscriber": false, "payg": { "account_credits": 500.0, "token_used": 87.61, "token_total": "unlimited", "token_remaining": "unlimited", "token_is_unlimited": true } } ``` ### Response (Subscription) When the account has an active subscription plan: ```json { "object": "billing_credits", "is_subscriber": true, "payg": { "account_credits": 9.98, "token_used": 0.05, "token_total": 1.0, "token_remaining": 0.95, "token_is_unlimited": false }, "subscription": { "plan_type": "lite", "status": "active", "cycle_quota_limit": 600, "cycle_quota_used": 10, "cycle_quota_remaining": 590, "cycle_start": "2026-03-16T10:02:35Z", "cycle_end": "2026-04-16T10:02:35Z", "payg_fallback_enabled": false } } ``` ### Response (Subscription with PAYG Fallback) When the subscription has PAYG overage enabled: ```json { "object": "billing_credits", "is_subscriber": true, "payg": { "account_credits": 3.0, "token_used": 0.5, "token_total": 3.5, "token_remaining": 3.0, "token_is_unlimited": false }, "subscription": { "plan_type": "max", "status": "active", "cycle_quota_limit": 5000, "cycle_quota_used": 5000, "cycle_quota_remaining": 0, "cycle_start": "2026-03-01T00:00:00Z", "cycle_end": "2026-04-01T00:00:00Z", "payg_fallback_enabled": true, "payg_spent_usd": 2.5, "payg_limit_usd": 10.0 } } ``` ### Response Fields #### Top Level | Field | Type | Description | |-------|------|-------------| | `object` | string | Always `"billing_credits"` | | `is_subscriber` | boolean | Whether the account has an active subscription | | `payg` | object | PAYG credit balance (always present) | | `subscription` | object | Subscription quota details (only present when subscribed) | #### `payg` Object — Account Level | Field | Type | Description | |-------|------|-------------| | `account_credits` | number | Remaining credits for the entire account in USD | #### `payg` Object — Token Level | Field | Type | Description | |-------|------|-------------| | `token_used` | number | Credits consumed by this token in USD | | `token_total` | number \| `"unlimited"` | Total credits allocated to this token (remaining + used). `"unlimited"` when token has no quota limit | | `token_remaining` | number \| `"unlimited"` | Remaining credits for this token in USD. `"unlimited"` when token has no quota limit | | `token_is_unlimited` | boolean | Whether this token has unlimited quota | | `token_monthly_limit_usd` | number | Monthly spending limit for this token in USD (only present if configured) | | `token_monthly_used_usd` | number | Spending by this token in current month in USD (only present if limit configured) | | `monthly_reset_day` | integer | Day of month when the monthly counter resets (only present if limit configured) | #### `subscription` Object | Field | Type | Description | |-------|------|-------------| | `plan_type` | string | Plan name: `lite`, `pro`, or `max` | | `status` | string | Subscription status: `active`, `suspended`, or `cancelled` | | `cycle_quota_limit` | integer | Total quota allocation for the current billing cycle | | `cycle_quota_used` | integer | Quota consumed in the current cycle | | `cycle_quota_remaining` | integer | Remaining quota in the current cycle | | `cycle_start` | string | Current cycle start date (ISO 8601) | | `cycle_end` | string | Current cycle end date (ISO 8601) | | `payg_fallback_enabled` | boolean | Whether PAYG overage billing is enabled when cycle quota is exhausted | | `payg_spent_usd` | number | PAYG overage spent this cycle in USD (only present when fallback enabled) | | `payg_limit_usd` | number | PAYG overage spending limit in USD (only present when fallback enabled and limit set) | ### Token-Level vs Account-Level The `payg` section separates **account-level** and **token-level** information: - **`account_credits`**: The total remaining balance for the user account, shared across all tokens. - **`token_*` fields**: Quota specific to the API key used in the request. Each token can have its own quota allocation and limits. When a token is set to unlimited (`token_is_unlimited: true`), `token_remaining` and `token_total` return `"unlimited"` since the token itself has no cap — but `account_credits` still shows the real account balance. ### Python Example ```python import requests response = requests.get( "https://api.apertis.ai/v1/dashboard/billing/credits", headers={"Authorization": "Bearer sk-your-api-key"} ) credits = response.json() payg = credits["payg"] print(f"Account Balance: ${payg['account_credits']:.2f} USD") if payg["token_is_unlimited"]: print("Token: unlimited") else: print(f"Token: ${payg['token_remaining']:.2f} / ${payg['token_total']:.2f} USD") if credits["is_subscriber"]: sub = credits["subscription"] print(f"Plan: {sub['plan_type']}") print(f"Quota: {sub['cycle_quota_used']}/{sub['cycle_quota_limit']} used") ``` ### Node.js Example ```javascript const response = await fetch( 'https://api.apertis.ai/v1/dashboard/billing/credits', { headers: { Authorization: 'Bearer sk-your-api-key' } } ); const credits = await response.json(); const { payg } = credits; console.log(`Account: $${payg.account_credits.toFixed(2)}`); if (payg.token_is_unlimited) { console.log('Token: unlimited'); } else { console.log(`Token: $${payg.token_remaining.toFixed(2)} / $${payg.token_total.toFixed(2)}`); } if (credits.is_subscriber) { const { plan_type, cycle_quota_used, cycle_quota_limit } = credits.subscription; console.log(`${plan_type}: ${cycle_quota_used}/${cycle_quota_limit} quota used`); } ``` --- Source: https://docs.apertis.ai/help/ideas # Ideas — Community Feedback Apertis includes a built-in community feedback platform where you can submit feature requests, report issues, and vote on proposals that matter to you. ## Features - **Browse proposals** — View all submissions organized by status: In Progress, Planned, Open, Completed, and Declined - **Submit new requests** — Create feature requests, model requests, bug reports, and more with category and priority selection - **Vote** — Upvote proposals you support to help us understand community priorities - **Discuss** — Leave comments on any proposal to share your thoughts ## Categories | Category | Description | |----------|-------------| | Model Request | Request support for a new AI model | | Feature Request | Suggest a new platform feature | | Subscription Plan | Feedback on plans and pricing | | SDK & Integration | Requests for SDK or tool integrations | | Bug Report | Report issues or unexpected behavior | | Other | Anything else | ## Status Workflow Proposals move through the following stages: ``` Open → Planned → In Progress → Completed → Declined ``` | Status | Meaning | |--------|---------| | **Open** | Submitted, awaiting review | | **Planned** | Approved, scheduled for development | | **In Progress** | Currently being worked on | | **Completed** | Shipped | | **Declined** | Not planned at this time | ## How to Use 1. Sign in to your Apertis account 2. Go to the [Ideas page](https://apertis.ai/ideas) 3. Click **New request** to submit a proposal, or upvote existing ones 4. Click on any proposal to view details and join the discussion