Stablev1

REST API

Works from any runtime that can make HTTPS calls

Ruby / PHP / .NET / Rust / Java / shell users go straight to REST. All endpoints + parameter tables live in the API Reference; this page provides language examples.

Base information

Base URLhttps://api.skyaiapp.com
Sandboxhttps://api-sandbox.skyaiapp.com
AuthAuthorization: Bearer $KEY
Content-Typeapplication/json; UTF-8
TLSTLS 1.2+
TimestampsRFC 3339 (UTC)

Authentication

Authorization: Bearer sk_live_01JEXAMPLE...
# Sandbox / CI:
Authorization: Bearer sk_test_01JEXAMPLE...

Key handling details (rotation, leaks): Security guide

POST /v1/route

curl https://api.skyaiapp.com/v1/route \
  -H "Authorization: Bearer $SKYAIAPP_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: req_$(date +%s)" \
  -d '{
    "goal": "quality",
    "strategy": "quality-first",
    "messages": [
      { "role": "system", "content": "You are a senior travel planner." },
      { "role": "user",   "content": "Plan a 7-day European trip in October." }
    ],
    "fallback": { "models": ["claude-opus-4.7", "gemini-3.1-pro"], "maxRetries": 2 },
    "budget":   { "maxCostUsd": 0.05 },
    "cache":    true,
    "timeout_ms": 30000,
    "metadata": { "tenant": "acme-corp", "workflow": "trip-planner" }
  }'

Full parameter table and response schema: API Reference · Routing

More endpoints

  • GET /v1/modelsList routable models with pricing, context window, regions.
  • POST /v1/agents/runRun a multi-step agent.
  • GET /v1/traces/{id}Fetch a single trace's full span tree.
  • GET /v1/traces?from=&to=&filter=Filter traces by window / metadata (cursor paginated).
  • GET /v1/analytics/usageUsage, cost, token counts.
  • GET /v1/analytics/cacheCache hit rate + dollars saved.

Language examples

Ruby (Net::HTTP)

require "net/http"
require "json"

uri = URI("https://api.skyaiapp.com/v1/route")
req = Net::HTTP::Post.new(uri, {
  "Authorization"   => "Bearer #{ENV['SKYAIAPP_API_KEY']}",
  "Content-Type"    => "application/json",
  "Idempotency-Key" => "req_#{Time.now.to_i}",
})
req.body = {
  goal: "cost", strategy: "balanced",
  messages: [{ role: "user", content: "Hello!" }],
  budget: { maxCostUsd: 0.01 },
  timeout_ms: 15_000,
}.to_json

res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |h| h.request(req) }
abort "HTTP #{res.code}: #{res.body}" unless res.is_a?(Net::HTTPSuccess)
data = JSON.parse(res.body)
puts data.dig("routing", "selected_model"), data["output"]

PHP (Guzzle)

<?php
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://api.skyaiapp.com', 'timeout' => 30]);

try {
    $res = $client->post('/v1/route', [
        'headers' => [
            'Authorization'   => 'Bearer ' . getenv('SKYAIAPP_API_KEY'),
            'Idempotency-Key' => 'req_' . time(),
        ],
        'json' => [
            'goal'      => 'quality',
            'strategy'  => 'balanced',
            'messages'  => [['role' => 'user', 'content' => 'Hello!']],
            'budget'    => ['maxCostUsd' => 0.01],
            'timeout_ms'=> 15000,
        ],
    ]);
    $data = json_decode($res->getBody(), true);
    echo $data['routing']['selected_model'] . PHP_EOL . $data['output'];
} catch (\GuzzleHttp\Exception\ClientException $e) {
    $err = json_decode($e->getResponse()->getBody(), true);
    error_log("router error: " . $err['error']['code'] . " trace=" . $err['error']['trace_id']);
    throw $e;
}

.NET / C#

using System.Net.Http.Json;

var http = new HttpClient { BaseAddress = new Uri("https://api.skyaiapp.com") };
http.DefaultRequestHeaders.Authorization =
    new("Bearer", Environment.GetEnvironmentVariable("SKYAIAPP_API_KEY"));

var req = new {
    goal = "cost", strategy = "balanced",
    messages = new[] { new { role = "user", content = "Hello!" } },
    budget = new { maxCostUsd = 0.01 },
    timeout_ms = 15_000,
};

using var msg = new HttpRequestMessage(HttpMethod.Post, "/v1/route") {
    Content = JsonContent.Create(req),
};
msg.Headers.Add("Idempotency-Key", $"req_{DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()}");

var res = await http.SendAsync(msg);
res.EnsureSuccessStatusCode();
var body = await res.Content.ReadFromJsonAsync<JsonElement>();
Console.WriteLine(body.GetProperty("routing").GetProperty("selected_model"));

Rust (reqwest)

use reqwest::Client;
use serde_json::json;

let client = Client::new();
let res: serde_json::Value = client
    .post("https://api.skyaiapp.com/v1/route")
    .bearer_auth(std::env::var("SKYAIAPP_API_KEY")?)
    .header("Idempotency-Key", format!("req_{}", chrono::Utc::now().timestamp()))
    .json(&json!({
        "goal":     "quality",
        "strategy": "balanced",
        "messages": [{ "role": "user", "content": "Hello!" }],
        "budget":   { "maxCostUsd": 0.01 },
        "timeout_ms": 15_000,
    }))
    .timeout(std::time::Duration::from_secs(30))
    .send().await?
    .error_for_status()?
    .json().await?;

println!("model = {}", res["routing"]["selected_model"]);

Recommended practices

  • Always send Idempotency-Key — protects against double-billing on retries.
  • Always set timeout_ms (≤ 30s for user-facing; batch jobs can go higher).
  • Always log error.code and error.trace_id.
  • Do not retry on 4xx (except 408 / 429).
  • Use sk_test_ keys + sandbox URL for integration tests.

See also

Was this page helpful?

Let us know how we can improve

REST API | SkyAIApp Docs — SkyAIApp