One API
SDKs & clients
You do not need an Ocean-specific SDK to use the One API. The models plane is OpenAI-wire, so every OpenAI-compatible client works with a base-URL swap; the execution planes are plain HTTP+JSON and have official SDKs in TypeScript, Python, Go and Rust. This chapter lists the options, shortest path first.
Key throughout: $PLUNGE_API_KEY = ozk_, the one key for every plane. See
Authentication & keys.
OpenAI SDK (models plane)
Point the official OpenAI SDK at https://api.plungeai.com/v1 with your ozk_ key.
This covers /v1/chat/completions, /v1/embeddings, and /v1/models.
Python:
from openai import OpenAI
client = OpenAI(
api_key="<your ozk_ key>",
base_url="https://api.plungeai.com/v1",
default_headers={"x-trace-id": "00000000-0000-4000-8000-000000000006"}, # optional UUID
)
resp = client.chat.completions.create(
model="anthropic/claude-sonnet-4-6",
messages=[{"role": "user", "content": "Reply with exactly the word: pong"}],
max_tokens=16,
)TypeScript:
import OpenAI from 'openai'
const client = new OpenAI({
apiKey: process.env.PLUNGE_API_KEY,
baseURL: 'https://api.plungeai.com/v1',
defaultHeaders: { 'x-trace-id': crypto.randomUUID() }, // optional
})Ocean-only fields (models[], sort, @preset/<slug>) pass through as extra body params —
use the SDK's extra_body (Python) or just add them to the request object (TypeScript). See
Models plane.
Official SDKs (execution planes)
Four packages, one shape: a client that reads the key from PLUNGEAI_API_KEY, one method per
operation (client.agents.execute(…)), typed errors by status, retries (2, with backoff, on
connection errors, 408, 429 and ≥ 500 — never on 409 or 424, which are semantic answers), a
10-minute timeout, server-sent-event streams, offset pages that iterate across pages, raw-response
access and get/post/… escape hatches. The curl examples in this guide use $PLUNGE_API_KEY; the
SDKs' variable is PLUNGEAI_API_KEY, the CLI's too. All four target One API 2.5.0 (package version
1.0.0); their resource layers are generated from this document, so every operation listed here has
a method. Source: sdk/ in the platform repository.
| Language | Package | Install |
|---|---|---|
| TypeScript | @plungeai/one-api (npm) | npm install @plungeai/one-api |
| Python | plungeai (PyPI; sync + async over httpx) | pip install plungeai |
| Go | github.com/PlungeAI/plungeai-go (stdlib only) | go get github.com/PlungeAI/plungeai-go |
| Rust | plungeai (crates.io; async over reqwest) | cargo add plungeai |
Release status: npm view @plungeai/one-api version, pip index versions plungeai,
go list -m -versions github.com/PlungeAI/plungeai-go, cargo search plungeai; until a registry
answers, build the package from its directory under sdk/.
TypeScript
import PlungeAI from '@plungeai/one-api'
const client = new PlungeAI()
const me = await client.whoami()
const run = await client.agents.execute('one-agent', { prompt: 'Reply with exactly the word: pong', max_tokens: 32 })
const hits = await client.discovery.search({ q: 'web search', kind: 'agents', limit: 3 })
const stream = await client.workflows.executeInlineStream({ workflow }) // workflow: CNL YAML string
for await (const event of stream) console.log(event.event, event.data)
for await (const w of client.workflows.list()) console.log(w) // walks every page
try {
await client.agents.execute('no-such-agent', { prompt: 'hi' })
} catch (err) {
if (err instanceof PlungeAI.NotFoundError) console.log(err.status, err.code, err.requestID)
else throw err
}Each status has its own error class (BadRequestError … FailedDependencyError for 424,
InternalServerError for ≥ 500), all exported on PlungeAI; a per-request idempotencyKey is
sent as x-trace-id, so a replayed run answers 409 duplicate_execution_id instead of starting
twice. Zero runtime dependencies, ESM and CJS.
Python
from plungeai import PlungeAI, NotFoundError
client = PlungeAI() # or AsyncPlungeAI() — same methods, awaited
me = client.whoami()
run = client.agents.execute("one-agent", prompt="Reply with exactly the word: pong", max_tokens=32)
hits = client.discovery.search(q="web search", kind="agents", limit=3)
stream = client.workflows.execute_inline_stream(workflow=workflow) # workflow: CNL YAML string
for event in stream:
print(event.event, event.data)
for w in client.workflows.list(): # walks every page
print(w)
try:
client.agents.execute("no-such-agent", prompt="hi")
except NotFoundError as err:
print(err.status_code, err.code, err.request_id)Method names are snake_case (execute_inline_stream); request fields are keyword arguments
typed as TypedDicts; responses are plain dicts typed the same way. stream=True on
agents.execute and chat.completions.create returns a Stream. Per-call options:
extra_headers, extra_query, timeout, max_retries, idempotency_key;
client.agents.with_raw_response.execute(...) gives the status, headers and request id. One
runtime dependency: httpx. Python ≥ 3.10.
Go
import (
"context"
"errors"
"fmt"
"github.com/PlungeAI/plungeai-go"
)
client := plungeai.NewClient() // plungeai.WithAPIKey(…) to override PLUNGEAI_API_KEY
ctx := context.Background()
me, err := client.Whoami(ctx)
run, err := client.Agents.Execute(ctx, "one-agent", plungeai.AgentExecuteParams{Prompt: "Reply with exactly the word: pong", MaxTokens: plungeai.Int(32)})
hits, err := client.Discovery.Search(ctx, plungeai.DiscoverySearchParams{Q: "web search", Kind: "agents", Limit: plungeai.Int(3)})
stream, err := client.Workflows.ExecuteInlineStream(ctx, plungeai.WorkflowExecuteInlineStreamParams{Workflow: workflow})
for stream.Next() {
ev := stream.Current()
fmt.Println(ev.Event, ev.Data)
}
err = stream.Err()
pager := client.Workflows.ListAutoPaging(ctx, plungeai.WorkflowListParams{}) // walks every page
for pager.Next() {
fmt.Println(pager.Current())
}
_, err = client.Agents.Execute(ctx, "no-such-agent", plungeai.AgentExecuteParams{Prompt: "hi"})
var apiErr *plungeai.Error
if errors.As(err, &apiErr) && apiErr.StatusCode == 404 {
fmt.Println(apiErr.StatusCode, apiErr.Code, apiErr.RequestID)
}One package, no sub-packages: options are plungeai.WithAPIKey, WithBaseURL, WithMaxRetries,
WithRequestTimeout, WithHeader, WithQuery, WithIdempotencyKey, WithResponseInto, usable
on NewClient and on every call. Streaming variants of the stream-flag operations are
Agents.ExecuteStreaming and Chat.Completions.CreateStreaming; list operations have a
…AutoPaging twin. Errors are one type, *plungeai.Error, classified by StatusCode. Standard
library only. Go ≥ 1.24.
Rust
use futures_util::StreamExt;
use plungeai::{types::*, Client};
let client = Client::new()?; // or Client::builder().api_key(…).build()?
let me = client.whoami().await?;
let run = client
.agents()
.execute("one-agent", &AgentExecuteParams { prompt: Some("Reply with exactly the word: pong".into()), max_tokens: Some(32), ..Default::default() })
.await?;
let hits = client.discovery().search(&DiscoverySearchParams { q: Some("web search".into()), kind: Some(DiscoverySearchParamsKind::Agents), limit: Some(3), ..Default::default() }).await?;
let mut stream = client.workflows().execute_inline_stream(&WorkflowExecuteInlineStreamParams { workflow: workflow.into(), ..Default::default() }).await?;
while let Some(event) = stream.next().await {
let event = event?;
println!("{:?} {}", event.event, event.data);
}
let mut items = client.workflows().list(&Default::default()).await?.into_stream(); // walks every page
while let Some(w) = items.next().await {
println!("{:?}", w?);
}
match client.agents().execute("no-such-agent", &AgentExecuteParams { prompt: Some("hi".into()), ..Default::default() }).await {
Err(e) if e.is_not_found() => println!("{:?} {:?}", e.status(), e.request_id()),
other => { other?; }
}Params and responses are serde structs under plungeai::types (optional fields Option<T>,
response enums carry an Unknown variant so a new server value never breaks a client). Errors are
one enum, plungeai::Error, with status() and is_not_found()-style predicates; streaming
variants are execute_stream; pages expose has_next_page(), next_page() and into_stream(), carry
request_id and headers, and have a list_raw form.
Async over reqwest (rustls); tokio runtime. Rust ≥ 1.85.
OpenAI-compatible frameworks (models plane)
Any framework that accepts an OpenAI base URL works. Set base URL
https://api.plungeai.com/v1 and your ozk_ key.
LiteLLM:
import litellm
resp = litellm.completion(
model="openai/anthropic/claude-sonnet-4-6", # openai/ tells LiteLLM to use the OpenAI wire
api_base="https://api.plungeai.com/v1",
api_key="<your ozk_ key>",
messages=[{"role": "user", "content": "pong?"}],
)Vercel AI SDK:
import { createOpenAI } from '@ai-sdk/openai'
const ocean = createOpenAI({
baseURL: 'https://api.plungeai.com/v1',
apiKey: process.env.PLUNGE_API_KEY,
})
const model = ocean('anthropic/claude-sonnet-4-6')LangChain (Python):
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(
model="anthropic/claude-sonnet-4-6",
base_url="https://api.plungeai.com/v1",
api_key="<your ozk_ key>",
)MCP clients (the platform as an MCP server)
The platform is itself an MCP server. Point any MCP client at the inbound Streamable-HTTP
endpoint with your ozk_ key. There are two equivalent URLs (both live-verified, both
return the same tool table): https://api.plungeai.com/v1/mcp (the One API's pass-through,
which forwards your key) and https://mcp.plungeai.com/v1 (the MCP gateway directly).
Claude Code:
claude mcp add --transport http plungeai https://api.plungeai.com/v1/mcp \
--header "Authorization: Bearer $PLUNGE_API_KEY"Claude Desktop / other clients that speak mcp-remote:
{
"mcpServers": {
"plungeai": {
"command": "npx",
"args": ["mcp-remote", "https://api.plungeai.com/v1/mcp",
"--header", "Authorization: Bearer ${PLUNGE_API_KEY}"]
}
}
}You can confirm the tool table over plain curl (JSON-RPC tools/list) — live-verified:
curl -s -X POST https://api.plungeai.com/v1/mcp \
-H "Authorization: Bearer $PLUNGE_API_KEY" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","method":"tools/list","id":1}'See MCP plane for inbound tools and outbound MCP runs, and Connecting clients (opens in a new tab) in the MCP developer guide for every supported client's config and how to verify it.
Ocean CLI (ocean-cli)
A non-interactive terminal client for scripts, CI and other agents: ocean-cli <noun> <verb>,
--json on every verb, stable exit codes. Package @plungeai/ocean (not yet on npm; run it from
the repo with npm run ocean-cli -- --help). ocean-cli login stores your ozk_ key; the
Studio nouns also need ocean-cli login --session. Each noun rides one of three surfaces:
| Surface | Credential | Nouns |
|---|---|---|
One API (https://api.plungeai.com/v1) | ozk_ key (X-API-Key; Authorization: Bearer on the models plane) | search, extract, answer, research, findall, monitor, enrich (through POST /v1/tools/parallel-agent/execute); agents; tools list|get|call; workflows run (waiting or --stream), workflows result|cancel; discovery; models; mcp (outbound runs); traces |
MCP (https://mcp.plungeai.com/v1) | ozk_ key (Authorization: Bearer) | workflows list|get|create|update|delete|versions|restore|build, workflows run --no-wait; executions; missions; chat; schedules; memory; learn; templates; tools call --async; whoami |
Studio session (https://studio.plungeai.com, https://dashboard.plungeai.com) | auth-session cookie | keys, balance, usage, nodes, campaigns, connections, secrets, documents, teams, projects, executions export |
ocean-cli doctor checks all three hosts and both credentials. Full command reference:
apps/ocean-cli/README.md.
curl conventions used in this guide
- Base URL is always
https://api.plungeai.com. - Auth is
-H "Authorization: Bearer $PLUNGE_API_KEY"on every plane;-H "X-API-Key: $PLUNGE_API_KEY"also works on the execution planes. - JSON bodies use
-H "Content-Type: application/json"; workflow YAML can be posted with-H "Content-Type: text/yaml" --data-binary @workflow.yaml. - Streaming examples add
-Nso curl does not buffer the SSE. - Correlation: add
-H "x-trace-id: <a UUID>"to tie several calls into one trace.
A plain execution-plane list call (the shape every ozk_ GET follows) is live-verified:
curl -s "https://api.plungeai.com/v1/agents?limit=3" \
-H "Authorization: Bearer $PLUNGE_API_KEY"