Skip to main content

Base URL

https://api.verba.ink

Quick start

1

Create an API key

Go to Verba Settings -> Security -> API Keys and create a key.
2

Pick a character

Use your character vanity slug or vanity URL (for example mycharacter_abc or https://verba.ink/v/mycharacter_abc).
3

Call an endpoint

Use POST /v1/response for text replies, or POST /v1/image for image generation.

Authentication

Send your API key in either header:
  • Authorization: Bearer vka_...
  • x-api-key: vka_...
If no key is provided, or the key is invalid/revoked, the API returns 401.

Character identifier

Both endpoints use character (not verb_id). Accepted formats:
  • Vanity slug: mycharacter_abc
  • Vanity path: /v/mycharacter_abc
  • Full vanity URL: https://verba.ink/v/mycharacter_abc

Endpoints

  • POST /v1/response for text chat completions
  • POST /v1/image for image generation

POST /v1/response

Text completion endpoint. You can optionally attach image URLs for vision-enabled prompting.
Supports both standard JSON and streaming. Set stream: true to receive Server-Sent Events (SSE) chunks.
Streaming on /v1/response is available for Plus, Pro, and Ultra plans.
Required:
  • character string (vanity URL or vanity slug)
  • messages array (role + content)
Optional:
  • session_id string
  • temperature number (0..2)
  • top_p number (0..1)
  • max_tokens number
  • stream boolean (true for SSE stream, default false)
  • image_urls array of image URLs (http/https, max 4)
messages[].content supports:
  • String text content
  • Array parts with:
    • { "type": "text", "text": "..." }
    • { "type": "image_url", "image_url": { "url": "https://..." } }
You can pass image URLs either in messages[].content or top-level image_urls (both are merged, max 4 total).

Request examples

curl https://api.verba.ink/v1/response \
  -X POST \
  -H "Authorization: Bearer $VERBA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "character": "mycharacter_abc",
    "messages": [
      { "role": "user", "content": "Tell me a short story about space pirates." }
    ],
    "stream": false
  }'

Vision input example

{
  "character": "mycharacter_abc",
  "messages": [
    {
      "role": "user",
      "content": [
        { "type": "text", "text": "Describe what is happening here." },
        { "type": "image_url", "image_url": { "url": "https://example.com/city.jpg" } }
      ]
    }
  ],
  "stream": false
}

Example response

{
  "id": "chatcmpl_...",
  "object": "chat.completion",
  "created": 1739730000,
  "model": "m_free_a",
  "character": "mycharacter_abc",
  "session_id": "sess_...",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "..."
      },
      "finish_reason": "stop"
    }
  ],
  "usage": {
    "prompt_tokens": 0,
    "completion_tokens": 0,
    "total_tokens": 0
  }
}

Streaming (stream: true)

With stream: true, /v1/response returns text/event-stream and emits data: events.
  • Each event is an OpenAI-style chat.completion.chunk payload.
  • The final event is data: [DONE].
  • You should concatenate choices[0].delta.content chunks to build the assistant message.
  • Free plans return 403 stream_plan_upgrade_required when stream: true is used.
Example chunk:
{
  "id": "chatcmpl_...",
  "object": "chat.completion.chunk",
  "created": 1739730000,
  "model": "m_free_a",
  "character": "mycharacter_abc",
  "session_id": "sess_...",
  "choices": [
    {
      "index": 0,
      "delta": { "content": "Hello" },
      "finish_reason": null
    }
  ]
}
Final chunk:
{
  "id": "chatcmpl_...",
  "object": "chat.completion.chunk",
  "created": 1739730000,
  "model": "m_free_a",
  "character": "mycharacter_abc",
  "session_id": "sess_...",
  "choices": [
    {
      "index": 0,
      "delta": {},
      "finish_reason": "stop"
    }
  ],
  "usage": {
    "prompt_tokens": 0,
    "completion_tokens": 0,
    "total_tokens": 0
  }
}
Then:
data: [DONE]

Streaming request examples

curl -N https://api.verba.ink/v1/response \
  -X POST \
  -H "Authorization: Bearer $VERBA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "character": "mycharacter_abc",
    "messages": [
      { "role": "user", "content": "Give me a short greeting." }
    ],
    "stream": true
  }'

POST /v1/image

Generates one image URL. Required:
  • character string (vanity URL or vanity slug)
  • prompt string
Optional:
  • session_id string
  • image_urls string array (reference images)
  • size (1024x1024 only)
  • response_format (url only)

Request examples

curl https://api.verba.ink/v1/image \
  -X POST \
  -H "Authorization: Bearer $VERBA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "character": "mycharacter_abc",
    "prompt": "A cinematic portrait of a cyberpunk fox detective",
    "size": "1024x1024",
    "response_format": "url"
  }'

Example response

{
  "created": 1739730000,
  "character": "mycharacter_abc",
  "session_id": "sess_...",
  "model": "m_free_a",
  "revised_prompt": "A cinematic portrait of a cyberpunk fox detective",
  "data": [
    {
      "url": "https://api.verba.ink/uploads/generated-images/img_...png"
    }
  ]
}

Sessions and memory

  • session_id is optional.
  • If omitted, Verba generates one and returns it.
  • Reusing the same session_id preserves API conversation context for that caller + verb pair.
  • API memory is stored in conversation history only (not DM or message logs).

Access, billing, and limits

  • API access is available to all users.
  • Verb privacy still applies:
    • Private characters: owner only
    • Public characters: any API caller
  • Billing and tier/model enforcement are applied to the character owner account.
  • /v1/* uses account rate limits via the same per-user limiter model.

Message format (/v1/response)

  • messages must be an array.
  • Include at least one user message.
  • Allowed roles: user, assistant, system (assistant maps internally to model role).
  • messages[].content supports text and image_url parts.
  • Top-level image_urls is also supported.
  • Combined image URL limit is 4.

Error model

Errors return JSON with a top-level message and an error object.
{
  "message": "stream must be a boolean",
  "error": {
    "message": "stream must be a boolean",
    "type": "invalid_stream",
    "code": 400
  }
}
  • error.type: stable machine-readable error type (for program logic)
  • error.code: HTTP status code
Common error cases:
  • 401 invalid_api_key for missing/invalid/revoked API key
  • 403 verb_access_denied for private character access by non-owner
  • 403 stream_plan_upgrade_required when free plans request stream: true
  • 403 insufficient_credits when the character owner has no credits for the selected model
  • 400 invalid_stream when stream is not a boolean
  • 400 invalid_image_urls when image URL payload format is invalid
  • 400 blocked_image_url when image URL targets local/private network hosts
  • 400 too_many_image_urls when more than 4 image URLs are provided
  • 400 invalid_size when /v1/image size is not 1024x1024