SDKs

HTTP / cURL

Everything is plain JSON over HTTPS. Any language with an HTTP client can call it.

Anatomy of a request#

curl https://api.xkiro.com/v1/chat/completions \
  -H "Authorization: Bearer $XKIRO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "openai/gpt-5.6-sol",
    "messages": [{ "role": "user", "content": "Hello" }]
  }'
  • Base URLhttps://api.xkiro.com, with /v1 as the first path segment.
  • AuthAuthorization: Bearer or x-api-key, whichever you prefer.
  • Body — JSON, except /v1/images/edits which is multipart because it carries a file.

Streaming over raw HTTP#

Add -N so curl does not buffer, and read the data: lines as they arrive.

curl -N https://api.xkiro.com/v1/chat/completions \
  -H "Authorization: Bearer $XKIRO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "openai/gpt-5.6-sol",
    "stream": true,
    "messages": [{ "role": "user", "content": "Count to five." }]
  }'

Events can split across chunks

A network read can land in the middle of an event. Buffer the tail and only parse complete events — parsing half of one throws. There is a correct reader on Streaming.

Other languages#

Go

package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
	"os"
	"time"
)

func main() {
	body, _ := json.Marshal(map[string]any{
		"model": "openai/gpt-5.6-sol",
		"messages": []map[string]string{
			{"role": "user", "content": "Hello"},
		},
	})

	req, _ := http.NewRequest("POST",
		"https://api.xkiro.com/v1/chat/completions", bytes.NewReader(body))
	req.Header.Set("Authorization", "Bearer "+os.Getenv("XKIRO_API_KEY"))
	req.Header.Set("Content-Type", "application/json")

	// Above the 95-second blocking limit, so a slow-but-healthy request is not
	// killed by the client before the server answers.
	client := &http.Client{Timeout: 120 * time.Second}
	res, err := client.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()

	var out map[string]any
	json.NewDecoder(res.Body).Decode(&out)
	fmt.Println(out)
}

PHP

<?php
$ch = curl_init("https://api.xkiro.com/v1/chat/completions");

curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT        => 120,
    CURLOPT_HTTPHEADER     => [
        "Authorization: Bearer " . getenv("XKIRO_API_KEY"),
        "Content-Type: application/json",
    ],
    CURLOPT_POSTFIELDS => json_encode([
        "model"    => "openai/gpt-5.6-sol",
        "messages" => [["role" => "user", "content" => "Hello"]],
    ]),
]);

$response = json_decode(curl_exec($ch), true);
echo $response["choices"][0]["message"]["content"];

Ruby

require "net/http"
require "json"

uri = URI("https://api.xkiro.com/v1/chat/completions")

http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.read_timeout = 120

req = Net::HTTP::Post.new(uri)
req["Authorization"] = "Bearer #{ENV['XKIRO_API_KEY']}"
req["Content-Type"] = "application/json"
req.body = {
  model: "openai/gpt-5.6-sol",
  messages: [{ role: "user", content: "Hello" }]
}.to_json

puts JSON.parse(http.request(req).body).dig("choices", 0, "message", "content")

Checklist for a hand-rolled client#

  • Timeout above 95 seconds for blocking calls, so your client does not give up before the server does.
  • Retry only 429, 500, 502 and 503, with exponential backoff and jitter. See Error codes.
  • Read to the end of a stream. Usage totals arrive on the final event; stopping early records zero.
  • Ignore unknown fields. New ones are added without a version bump, and a strict parser turns an additive change into an outage.
  • Never log the key. Not in request dumps, not in error reports.