SDKs
Official Python and Node.js clients — install once, and every endpoint is a method call.
predictAI ships official SDKs for Python (predict-ai-python-sdk) and Node.js / TypeScript (predict-ai-typescript-sdk). They wrap the whole API — every endpoint in these docs is available as a method — and handle authentication, workspace scoping, JSON, and the error envelope for you.
Every code sample in these docs has cURL, Python, and Node tabs. Pick your language once — the choice sticks across every page. For complete, runnable walkthroughs built with the SDKs, see the Examples section.
Not writing code at all? The official MCP server gives AI agents (Claude, Cursor, ChatGPT) the same full platform access as the SDKs — one URL, OAuth built in.
Install
pip install predictainpm install @predictai/sdkThe Node SDK is written in TypeScript, ships type declarations, and runs on
Node 18+ (it uses the built-in fetch).
Create a client
Both clients take an API token and an
optional default workspace — the workspace is sent
as X-Workspace-Id on every request, exactly like the header in the cURL
samples.
from predictai import PredictAI
client = PredictAI(
token="pa_live_...", # from POST /v1/user/api-tokens
workspace_id="ws_...", # default workspace scope (optional)
# base_url="https://api.predict.ai", # optional; redirects are followed automatically
)import { PredictAI } from "@predictai/sdk";
const client = new PredictAI({
token: "pa_live_...", // from POST /v1/user/api-tokens
workspaceId: "ws_...", // default workspace scope (optional)
// baseUrl: "https://api.predict.ai", // optional; redirects are followed automatically
});Both clients default to https://api.predict.ai, which works for every
account: if your data lives in another region, the client follows the
platform's redirect automatically and caches your home base URL. Passing
the base URL shown next to your API token in the app (as base_url /
baseUrl) just skips that first hop.
How methods are named
Methods are generated from the OpenAPI spec, so the mapping from any endpoint in these docs to a method call is mechanical:
- Section — the first path segment picks the namespace, mirroring the
docs sidebar:
client.signals,client.segments,client.pipelines,client.models,client.foundation,client.byom,client.trainings,client.deployments,client.inference,client.goals,client.ingestion,client.billing,client.workspaces,client.organizations,client.account,client.notifications. - Method — the HTTP method plus the path, joined with underscores;
path parameters become
by_<name>and are passed as positional arguments. Node uses the same names in camelCase.
| Endpoint | Python | Node |
|---|---|---|
POST /v1/signal/push | client.signals.post_signal_push(json={...}) | client.signals.postSignalPush({ json: {...} }) |
GET /v1/segment | client.segments.get_segment() | client.segments.getSegment() |
GET /v1/segment/{segment_id} | client.segments.get_segment_by_segment_id(id) | client.segments.getSegmentBySegmentId(id) |
PUT /v1/pipelines/{pipeline_id} | client.pipelines.put_pipelines_by_pipeline_id(id, json={...}) | client.pipelines.putPipelinesByPipelineId(id, { json: {...} }) |
Arguments work the same everywhere:
| Argument | Python | Node | What it carries |
|---|---|---|---|
| Path parameters required | positional | positional | IDs in the URL |
| Query string optional | params={...} | params: {...} | Filters, pagination |
| JSON body optional | json={...} | json: {...} | The request payload |
| File upload optional | files={...} | form: FormData | Multipart uploads (BYOM, file import) |
| Workspace override optional | workspace_id="..." | workspaceId: "..." | Per-call X-Workspace-Id |
Responses and errors
Methods return the decoded JSON body directly. Non-2xx responses raise
(Python) or throw (Node) a PredictAIError carrying the canonical
error envelope: message, code, the HTTP
status, and details.
from predictai import PredictAI, PredictAIError
client = PredictAI(token="pa_live_...", workspace_id="ws_...")
try:
data = client.pipelines.get_pipelines_by_pipeline_id("missing")
except PredictAIError as exc:
print(exc.status_code) # 404
print(exc.code) # "not_found"
print(exc.message) # human-readable description
print(exc.details) # surface-specific contextimport { PredictAI, PredictAIError } from "@predictai/sdk";
const client = new PredictAI({ token: "pa_live_...", workspaceId: "ws_..." });
try {
const data = await client.pipelines.getPipelinesByPipelineId("missing");
} catch (err) {
if (err instanceof PredictAIError) {
console.error(err.status); // 404
console.error(err.code); // "not_found"
console.error(err.message); // human-readable description
console.error(err.details); // surface-specific context
}
}Realtime (WebSockets)
Both SDKs wrap the realtime gateway behind
client.realtime — connect once, subscribe to
channels (workspace:ID, goal:ID,
user:ID), and iterate incoming events. The
SDK handles authentication, the welcome handshake, and app-level
keepalive pings for you.
# needs the optional dependency: pip install "predictai[realtime]"
rt = client.realtime.connect(channels=["workspace:ws_..."])
print(rt.welcome) # {"type": "welcome", "cell": "eu-1", ...}
rt.subscribe("goal:GOAL_ID") # add channels any time
for frame in rt.events(): # or iterate rt for ack/error frames too
print(frame["event"], frame["data"])
if frame["event"] == "deployment.updated":
break
rt.close()// Node 22+ / browsers use the built-in WebSocket. On older Node:
// npm install ws, then pass { webSocket: (await import("ws")).default }.
const rt = await client.realtime.connect({ channels: ["workspace:ws_..."] });
console.log(rt.welcome); // { type: "welcome", cell: "eu-1", ... }
rt.subscribe("goal:GOAL_ID"); // add channels any time
for await (const frame of rt.events()) { // or iterate rt for ack/error frames too
console.log(frame.event, frame.data);
if (frame.event === "deployment.updated") break;
}
rt.close();Delivery is best-effort and nothing is replayed after a disconnect —
treat events as invalidation hints and re-fetch state via REST, exactly
as the realtime guide describes. A 4401 close
means the token was rejected: replace it before reconnecting.
Streams, downloads, and anything else
For server-sent-event streams and binary downloads,
ask for the raw response instead of a decoded body — and if you ever need
an endpoint that doesn't have a generated method, request takes any
method and path with the same argument conventions:
# Raw response (SSE stream)
response = client.notifications.get_notifications_stream(stream=True)
for line in response.iter_lines():
print(line)
# Arbitrary request
data = client.request("POST", "/v1/signal/push", json={"key": "daily_sales", "value": 1.0})// Raw response (SSE stream)
const response = await client.notifications.getNotificationsStream({ raw: true });
// consume response.body as a stream
// Arbitrary request
const data = await client.request("POST", "/v1/signal/push", {
json: { key: "daily_sales", value: 1.0 },
});
