Rust snippets use ureq and serde_json (
ureq = { version = "2", features = ["json"] }, serde_json = "1"). Go snippets use the standard library only. Tab titles match Authentication and the Quickstart so your language choice stays in sync.What you will build
In this guide, you will build a minimal programmatic integration that can:- Authenticate with a bearer token
- Create a task
- Trigger execution
- Poll execution status
- Export results
Before you begin
| Requirement | Example |
|---|---|
| Task Service base URL | https://api.webscraping.titannet.io (no /api/v1 suffix in env vars below) |
| Bearer token | JWT or API key |
| Required scopes | tasks:read, tasks:write, executions:read, executions:write, data:read |
- cURL
- Go
- TypeScript
- Python
- Rust
export TITAN_API_URL="https://api.webscraping.titannet.io"
export TITAN_TOKEN="your-bearer-token"
// os.Setenv("TITAN_API_URL", "https://api.webscraping.titannet.io")
// os.Setenv("TITAN_TOKEN", "your-bearer-token")
process.env.TITAN_API_URL ??= "https://api.webscraping.titannet.io";
process.env.TITAN_TOKEN ??= "your-bearer-token";
import os
os.environ.setdefault("TITAN_API_URL", "https://api.webscraping.titannet.io")
os.environ.setdefault("TITAN_TOKEN", "your-bearer-token")
// std::env::set_var("TITAN_API_URL", "https://api.webscraping.titannet.io");
// std::env::set_var("TITAN_TOKEN", "your-bearer-token");
How Task Service fits into the platform
Response model
Most JSON endpoints return a success envelope with the typed payload underdata:
{
"success": true,
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "active",
"created_at": "2025-01-01T00:00:00Z",
"updated_at": "2025-01-01T00:00:00Z"
}
}
success: false and an error object with code and message (see API reference overview and HTTP errors and exceptions).
When building a client, parse and check the envelope first, then read fields from data. A few routes (for example GET …/results/export) stream files instead of JSON envelopes—treat those as raw HTTP bodies.
Main endpoint groups
| Capability | Common endpoint group |
|---|---|
| Task management | /api/v1/tasks |
| Execution control | /api/v1/executions |
| Templates | /api/v1/templates |
| Result access | /api/v1/executions/:id/results |
| Dataset access | /api/v1/tasks/:id/datasets and /api/v1/executions/:id/datasets |
| Media access | /api/v1/executions/:id/media |
Build a minimal client
These helpers assumeTITAN_API_URL is the origin (for example https://api.webscraping.titannet.io) and append /api/v1.
- cURL
- Go
- TypeScript
- Python
- Rust
# JSON body POST
titan_post() {
local path="$1"
shift
curl -sS -X POST "${TITAN_API_URL}/api/v1${path}" \
-H "Authorization: Bearer ${TITAN_TOKEN}" \
-H "Content-Type: application/json" \
-d "$@"
}
# GET with bearer
titan_get() {
curl -sS "${TITAN_API_URL}/api/v1$1" \
-H "Authorization: Bearer ${TITAN_TOKEN}"
}
func titanJSON(method, path string, body any, token, base string) ([]byte, error) {
var rdr io.Reader
if body != nil {
b, err := json.Marshal(body)
if err != nil {
return nil, err
}
rdr = bytes.NewReader(b)
}
req, err := http.NewRequest(method, base+"/api/v1"+path, rdr)
if err != nil {
return nil, err
}
req.Header.Set("Authorization", "Bearer "+token)
if body != nil {
req.Header.Set("Content-Type", "application/json")
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
return io.ReadAll(resp.Body)
}
const base = process.env.TITAN_API_URL!;
const token = process.env.TITAN_TOKEN!;
type SuccessEnvelope<T> = { success: true; data: T };
type ErrorEnvelope = {
success: false;
error: { code: string; message: string };
};
async function titanJson<T>(res: Response): Promise<T> {
const body = (await res.json()) as SuccessEnvelope<T> | ErrorEnvelope;
if (!res.ok || body.success === false) {
throw new Error(`HTTP ${res.status}: ${JSON.stringify(body)}`);
}
return body.data;
}
async function titanRequest<T>(
method: string,
path: string,
body?: unknown,
): Promise<T> {
const res = await fetch(`${base}/api/v1${path}`, {
method,
headers: {
Authorization: `Bearer ${token}`,
...(body !== undefined ? { "Content-Type": "application/json" } : {}),
},
body: body !== undefined ? JSON.stringify(body) : undefined,
});
return titanJson<T>(res);
}
import json
import os
import urllib.error
import urllib.request
def titan_request(method: str, path: str, body: dict | None = None) -> dict:
base = os.environ["TITAN_API_URL"]
token = os.environ["TITAN_TOKEN"]
data = None if body is None else json.dumps(body).encode()
req = urllib.request.Request(
f"{base}/api/v1{path}",
data=data,
headers={
"Authorization": f"Bearer {token}",
**({"Content-Type": "application/json"} if body is not None else {}),
},
method=method,
)
with urllib.request.urlopen(req) as resp:
payload = json.loads(resp.read().decode())
if not payload.get("success", False):
raise RuntimeError(payload)
return payload["data"]
use serde_json::Value;
fn titan_get_json(path: &str) -> Result<Value, Box<dyn std::error::Error>> {
let base = std::env::var("TITAN_API_URL").expect("TITAN_API_URL");
let token = std::env::var("TITAN_TOKEN").expect("TITAN_TOKEN");
let resp = ureq::get(format!("{base}/api/v1{path}"))
.set("Authorization", &format!("Bearer {token}"))
.call().expect("http");
let v: Value = serde_json::from_str(&resp.into_string().expect("body")).expect("json");
if v["success"] != true {
return Err(format!("{v}").into());
}
Ok(v["data"].clone())
}
fn titan_post_json(path: &str, body: Value) -> Result<Value, Box<dyn std::error::Error>> {
let base = std::env::var("TITAN_API_URL").expect("TITAN_API_URL");
let token = std::env::var("TITAN_TOKEN").expect("TITAN_TOKEN");
let resp = ureq::post(format!("{base}/api/v1{path}"))
.set("Authorization", &format!("Bearer {token}"))
.set("Content-Type", "application/json")
.send_json(body).expect("http");
let v: Value = serde_json::from_str(&resp.into_string().expect("body")).expect("json");
if v["success"] != true {
return Err(format!("{v}").into());
}
Ok(v["data"].clone())
}
Create a task programmatically
The table below describes the classic extraction shape most teams start with: known URLs plus an objective and output schema. Titan also supports modular and multi-step tasks whereurls, output_schema, and other fields are expressed per action or inside an execution_plan instead of only at the top level. For those patterns, see Action types overview and the Task Service operations in the API Reference tab.
| Field | Required (classic) | Meaning |
|---|---|---|
name | Yes | Human-readable task label |
urls | Yes | Target URLs |
objective | Yes | What the run should accomplish |
output_schema | Yes | JSON schema describing the structured result |
execution_type | Yes | single or scheduled |
schedule | For scheduled tasks | Cron string such as 0 * * * * |
Example: create a single-run task
- cURL
- Go
- TypeScript
- Python
- Rust
curl -sS -X POST "$TITAN_API_URL/api/v1/tasks" \
-H "Authorization: Bearer $TITAN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Product extraction",
"urls": ["https://example.com/product/1"],
"objective": "Extract product details",
"output_schema": {
"type": "object",
"properties": {
"title": { "type": "string" },
"price": { "type": "string" }
}
},
"execution_type": "single"
}'
body := map[string]any{
"name": "Product extraction",
"urls": []string{"https://example.com/product/1"},
"objective": "Extract product details",
"output_schema": map[string]any{
"type": "object",
"properties": map[string]any{
"title": map[string]string{"type": "string"},
"price": map[string]string{"type": "string"},
},
},
"execution_type": "single",
}
b, _ := json.Marshal(body)
req, _ := http.NewRequest("POST", os.Getenv("TITAN_API_URL")+"/api/v1/tasks", bytes.NewReader(b))
req.Header.Set("Authorization", "Bearer "+os.Getenv("TITAN_TOKEN"))
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
out, _ := io.ReadAll(resp.Body)
fmt.Println(string(out))
const base = process.env.TITAN_API_URL!;
const token = process.env.TITAN_TOKEN!;
const res = await fetch(`${base}/api/v1/tasks`, {
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
name: "Product extraction",
urls: ["https://example.com/product/1"],
objective: "Extract product details",
output_schema: {
type: "object",
properties: {
title: { type: "string" },
price: { type: "string" },
},
},
execution_type: "single",
}),
});
console.log(await res.text());
import json
import os
import urllib.request
payload = {
"name": "Product extraction",
"urls": ["https://example.com/product/1"],
"objective": "Extract product details",
"output_schema": {
"type": "object",
"properties": {
"title": {"type": "string"},
"price": {"type": "string"},
},
},
"execution_type": "single",
}
req = urllib.request.Request(
f"{os.environ['TITAN_API_URL']}/api/v1/tasks",
data=json.dumps(payload).encode(),
headers={
"Authorization": f"Bearer {os.environ['TITAN_TOKEN']}",
"Content-Type": "application/json",
},
method="POST",
)
with urllib.request.urlopen(req) as resp:
print(resp.read().decode())
use serde_json::json;
let base = std::env::var("TITAN_API_URL").expect("TITAN_API_URL");
let token = std::env::var("TITAN_TOKEN").expect("TITAN_TOKEN");
let body = json!({
"name": "Product extraction",
"urls": ["https://example.com/product/1"],
"objective": "Extract product details",
"output_schema": {
"type": "object",
"properties": {
"title": { "type": "string" },
"price": { "type": "string" }
}
},
"execution_type": "single"
});
let resp = ureq::post(format!("{base}/api/v1/tasks"))
.set("Authorization", &format!("Bearer {token}"))
.set("Content-Type", "application/json")
.send_json(body).expect("http");
println!("{}", resp.into_string().expect("body"));
Example: create a scheduled task
- cURL
- Go
- TypeScript
- Python
- Rust
curl -sS -X POST "$TITAN_API_URL/api/v1/tasks" \
-H "Authorization: Bearer $TITAN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Hourly price monitor",
"urls": ["https://example.com/product/1"],
"objective": "Track the current price",
"output_schema": {
"type": "object",
"properties": {
"price": { "type": "string" },
"timestamp": { "type": "string" }
}
},
"execution_type": "scheduled",
"schedule": "0 * * * *"
}'
body := map[string]any{
"name": "Hourly price monitor",
"urls": []string{"https://example.com/product/1"},
"objective": "Track the current price",
"output_schema": map[string]any{
"type": "object",
"properties": map[string]any{
"price": map[string]string{"type": "string"},
"timestamp": map[string]string{"type": "string"},
},
},
"execution_type": "scheduled",
"schedule": "0 * * * *",
}
b, _ := json.Marshal(body)
req, _ := http.NewRequest("POST", os.Getenv("TITAN_API_URL")+"/api/v1/tasks", bytes.NewReader(b))
req.Header.Set("Authorization", "Bearer "+os.Getenv("TITAN_TOKEN"))
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
out, _ := io.ReadAll(resp.Body)
fmt.Println(string(out))
const base = process.env.TITAN_API_URL!;
const token = process.env.TITAN_TOKEN!;
const res = await fetch(`${base}/api/v1/tasks`, {
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
name: "Hourly price monitor",
urls: ["https://example.com/product/1"],
objective: "Track the current price",
output_schema: {
type: "object",
properties: {
price: { type: "string" },
timestamp: { type: "string" },
},
},
execution_type: "scheduled",
schedule: "0 * * * *",
}),
});
console.log(await res.text());
import json
import os
import urllib.request
payload = {
"name": "Hourly price monitor",
"urls": ["https://example.com/product/1"],
"objective": "Track the current price",
"output_schema": {
"type": "object",
"properties": {
"price": {"type": "string"},
"timestamp": {"type": "string"},
},
},
"execution_type": "scheduled",
"schedule": "0 * * * *",
}
req = urllib.request.Request(
f"{os.environ['TITAN_API_URL']}/api/v1/tasks",
data=json.dumps(payload).encode(),
headers={
"Authorization": f"Bearer {os.environ['TITAN_TOKEN']}",
"Content-Type": "application/json",
},
method="POST",
)
with urllib.request.urlopen(req) as resp:
print(resp.read().decode())
use serde_json::json;
let base = std::env::var("TITAN_API_URL").expect("TITAN_API_URL");
let token = std::env::var("TITAN_TOKEN").expect("TITAN_TOKEN");
let body = json!({
"name": "Hourly price monitor",
"urls": ["https://example.com/product/1"],
"objective": "Track the current price",
"output_schema": {
"type": "object",
"properties": {
"price": { "type": "string" },
"timestamp": { "type": "string" }
}
},
"execution_type": "scheduled",
"schedule": "0 * * * *"
});
let resp = ureq::post(format!("{base}/api/v1/tasks"))
.set("Authorization", &format!("Bearer {token}"))
.set("Content-Type", "application/json")
.send_json(body).expect("http");
println!("{}", resp.into_string().expect("body"));
Trigger and monitor execution
| Pattern | Endpoint | Best for |
|---|---|---|
| Run saved task | POST /api/v1/tasks/:id/run | Standard user workflows |
| Trigger execution directly | POST /api/v1/executions | Execution-oriented integrations |
Run a saved task
- cURL
- Go
- TypeScript
- Python
- Rust
curl -sS -X POST "$TITAN_API_URL/api/v1/tasks/$TASK_ID/run" \
-H "Authorization: Bearer $TITAN_TOKEN"
req, _ := http.NewRequest(
"POST",
fmt.Sprintf("%s/api/v1/tasks/%s/run", os.Getenv("TITAN_API_URL"), os.Getenv("TASK_ID")),
nil,
)
req.Header.Set("Authorization", "Bearer "+os.Getenv("TITAN_TOKEN"))
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
out, _ := io.ReadAll(resp.Body)
fmt.Println(string(out))
const base = process.env.TITAN_API_URL!;
const token = process.env.TITAN_TOKEN!;
const taskId = process.env.TASK_ID!;
const res = await fetch(`${base}/api/v1/tasks/${taskId}/run`, {
method: "POST",
headers: { Authorization: `Bearer ${token}` },
});
console.log(await res.text());
import os
import urllib.request
tid = os.environ["TASK_ID"]
req = urllib.request.Request(
f"{os.environ['TITAN_API_URL']}/api/v1/tasks/{tid}/run",
headers={"Authorization": f"Bearer {os.environ['TITAN_TOKEN']}"},
method="POST",
)
with urllib.request.urlopen(req) as resp:
print(resp.read().decode())
let base = std::env::var("TITAN_API_URL").expect("TITAN_API_URL");
let token = std::env::var("TITAN_TOKEN").expect("TITAN_TOKEN");
let task_id = std::env::var("TASK_ID").expect("TASK_ID");
let resp = ureq::post(format!("{base}/api/v1/tasks/{task_id}/run"))
.set("Authorization", &format!("Bearer {token}"))
.send_json(serde_json::json!({})).expect("http");
println!("{}", resp.into_string().expect("body"));
ExecutionResponse in data; use data.id as the execution UUID for later GET /api/v1/executions/:id calls.
Poll execution state
- cURL
- Go
- TypeScript
- Python
- Rust
curl -sS "$TITAN_API_URL/api/v1/executions/$EXECUTION_ID" \
-H "Authorization: Bearer $TITAN_TOKEN"
req, _ := http.NewRequest(
"GET",
fmt.Sprintf("%s/api/v1/executions/%s", os.Getenv("TITAN_API_URL"), os.Getenv("EXECUTION_ID")),
nil,
)
req.Header.Set("Authorization", "Bearer "+os.Getenv("TITAN_TOKEN"))
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
out, _ := io.ReadAll(resp.Body)
fmt.Println(string(out))
const base = process.env.TITAN_API_URL!;
const token = process.env.TITAN_TOKEN!;
const executionId = process.env.EXECUTION_ID!;
const res = await fetch(`${base}/api/v1/executions/${executionId}`, {
headers: { Authorization: `Bearer ${token}` },
});
console.log(await res.text());
import os
import urllib.request
eid = os.environ["EXECUTION_ID"]
req = urllib.request.Request(
f"{os.environ['TITAN_API_URL']}/api/v1/executions/{eid}",
headers={"Authorization": f"Bearer {os.environ['TITAN_TOKEN']}"},
method="GET",
)
with urllib.request.urlopen(req) as resp:
print(resp.read().decode())
let base = std::env::var("TITAN_API_URL").expect("TITAN_API_URL");
let token = std::env::var("TITAN_TOKEN").expect("TITAN_TOKEN");
let execution_id = std::env::var("EXECUTION_ID").expect("EXECUTION_ID");
let resp = ureq::get(format!("{base}/api/v1/executions/{execution_id}"))
.set("Authorization", &format!("Bearer {token}"))
.call().expect("http");
println!("{}", resp.into_string().expect("body"));
data.status from the envelope on each poll.
Export results
When the execution reaches a terminal status such ascompleted, switch from execution monitoring to result access.
Result metadata (JSON envelope)
- cURL
- Go
- TypeScript
- Python
- Rust
curl -sS "$TITAN_API_URL/api/v1/executions/$EXECUTION_ID/results" \
-H "Authorization: Bearer $TITAN_TOKEN"
req, _ := http.NewRequest(
"GET",
fmt.Sprintf("%s/api/v1/executions/%s/results", os.Getenv("TITAN_API_URL"), os.Getenv("EXECUTION_ID")),
nil,
)
req.Header.Set("Authorization", "Bearer "+os.Getenv("TITAN_TOKEN"))
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
out, _ := io.ReadAll(resp.Body)
fmt.Println(string(out))
const base = process.env.TITAN_API_URL!;
const token = process.env.TITAN_TOKEN!;
const executionId = process.env.EXECUTION_ID!;
const res = await fetch(`${base}/api/v1/executions/${executionId}/results`, {
headers: { Authorization: `Bearer ${token}` },
});
console.log(await res.text());
import os
import urllib.request
eid = os.environ["EXECUTION_ID"]
req = urllib.request.Request(
f"{os.environ['TITAN_API_URL']}/api/v1/executions/{eid}/results",
headers={"Authorization": f"Bearer {os.environ['TITAN_TOKEN']}"},
method="GET",
)
with urllib.request.urlopen(req) as resp:
print(resp.read().decode())
let base = std::env::var("TITAN_API_URL").expect("TITAN_API_URL");
let token = std::env::var("TITAN_TOKEN").expect("TITAN_TOKEN");
let execution_id = std::env::var("EXECUTION_ID").expect("EXECUTION_ID");
let resp = ureq::get(format!("{base}/api/v1/executions/{execution_id}/results"))
.set("Authorization", &format!("Bearer {token}"))
.call().expect("http");
println!("{}", resp.into_string().expect("body"));
Export as JSON file (raw body, no envelope)
- cURL
- Go
- TypeScript
- Python
- Rust
curl -sS "$TITAN_API_URL/api/v1/executions/$EXECUTION_ID/results/export" \
-H "Authorization: Bearer $TITAN_TOKEN" \
-o results.json
req, _ := http.NewRequest(
"GET",
fmt.Sprintf("%s/api/v1/executions/%s/results/export", os.Getenv("TITAN_API_URL"), os.Getenv("EXECUTION_ID")),
nil,
)
req.Header.Set("Authorization", "Bearer "+os.Getenv("TITAN_TOKEN"))
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
out, _ := io.ReadAll(resp.Body)
os.WriteFile("results.json", out, 0o644)
const base = process.env.TITAN_API_URL!;
const token = process.env.TITAN_TOKEN!;
const executionId = process.env.EXECUTION_ID!;
const res = await fetch(`${base}/api/v1/executions/${executionId}/results/export`, {
headers: { Authorization: `Bearer ${token}` },
});
const fs = await import("node:fs/promises");
await fs.writeFile("results.json", Buffer.from(await res.arrayBuffer()));
import os
import urllib.request
eid = os.environ["EXECUTION_ID"]
req = urllib.request.Request(
f"{os.environ['TITAN_API_URL']}/api/v1/executions/{eid}/results/export",
headers={"Authorization": f"Bearer {os.environ['TITAN_TOKEN']}"},
method="GET",
)
with urllib.request.urlopen(req) as resp, open("results.json", "wb") as f:
f.write(resp.read())
use std::io::Read;
let base = std::env::var("TITAN_API_URL").expect("TITAN_API_URL");
let token = std::env::var("TITAN_TOKEN").expect("TITAN_TOKEN");
let execution_id = std::env::var("EXECUTION_ID").expect("EXECUTION_ID");
let resp = ureq::get(format!("{base}/api/v1/executions/{execution_id}/results/export"))
.set("Authorization", &format!("Bearer {token}"))
.call().expect("http");
let mut body = Vec::new();
resp.into_reader().read_to_end(&mut body).expect("read");
std::fs::write("results.json", body).expect("write");
Download as CSV
- cURL
- Go
- TypeScript
- Python
- Rust
curl -sS "$TITAN_API_URL/api/v1/executions/$EXECUTION_ID/results/download" \
-H "Authorization: Bearer $TITAN_TOKEN" -o results.csv
req, _ := http.NewRequest(
"GET",
fmt.Sprintf("%s/api/v1/executions/%s/results/download", os.Getenv("TITAN_API_URL"), os.Getenv("EXECUTION_ID")),
nil,
)
req.Header.Set("Authorization", "Bearer "+os.Getenv("TITAN_TOKEN"))
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
out, _ := io.ReadAll(resp.Body)
os.WriteFile("results.csv", out, 0o644)
const base = process.env.TITAN_API_URL!;
const token = process.env.TITAN_TOKEN!;
const executionId = process.env.EXECUTION_ID!;
const res = await fetch(`${base}/api/v1/executions/${executionId}/results/download`, {
headers: { Authorization: `Bearer ${token}` },
});
const fs = await import("node:fs/promises");
await fs.writeFile("results.csv", Buffer.from(await res.arrayBuffer()));
import os
import urllib.request
eid = os.environ["EXECUTION_ID"]
req = urllib.request.Request(
f"{os.environ['TITAN_API_URL']}/api/v1/executions/{eid}/results/download",
headers={"Authorization": f"Bearer {os.environ['TITAN_TOKEN']}"},
method="GET",
)
with urllib.request.urlopen(req) as resp, open("results.csv", "wb") as f:
f.write(resp.read())
use std::io::Read;
let base = std::env::var("TITAN_API_URL").expect("TITAN_API_URL");
let token = std::env::var("TITAN_TOKEN").expect("TITAN_TOKEN");
let execution_id = std::env::var("EXECUTION_ID").expect("EXECUTION_ID");
let resp = ureq::get(format!("{base}/api/v1/executions/{execution_id}/results/download"))
.set("Authorization", &format!("Bearer {token}"))
.call().expect("http");
let mut body = Vec::new();
resp.into_reader().read_to_end(&mut body).expect("read");
std::fs::write("results.csv", body).expect("write");
A full client flow
Copy the Build a minimal client snippet for your language, then run the flow below (sametitanRequest / titan_json names where applicable).
- cURL
- Go
- TypeScript
- Python
- Rust
# Requires titan_post / titan_get from "Build a minimal client" (cURL tab).
TASK_ID=$(titan_post /tasks "$(cat <<'JSON'
{
"name": "My task",
"urls": ["https://example.com"],
"objective": "Extract data",
"output_schema": { "type": "object", "properties": { "title": { "type": "string" } } },
"execution_type": "single"
}
JSON
)" | jq -r '.data.id')
RUN_JSON=$(curl -sS -X POST "${TITAN_API_URL}/api/v1/tasks/${TASK_ID}/run" \
-H "Authorization: Bearer ${TITAN_TOKEN}")
EXEC_ID=$(echo "$RUN_JSON" | jq -r '.data.id')
while true; do
ST=$(titan_get "/executions/${EXEC_ID}" | jq -r '.data.status')
echo "status=$ST"
echo "$ST" | grep -qE 'completed|failed|cancelled|completed_with_errors' && break
sleep 3
done
curl -sS "${TITAN_API_URL}/api/v1/executions/${EXEC_ID}/results/export" \
-H "Authorization: Bearer ${TITAN_TOKEN}" -o results.json
// Uses the same imports as the Go "Build a minimal client" helper (bytes, encoding/json, fmt, io, net/http, os, time).
base := os.Getenv("TITAN_API_URL")
tok := os.Getenv("TITAN_TOKEN")
createBody, _ := json.Marshal(map[string]any{
"name": "My task", "urls": []string{"https://example.com"},
"objective": "Extract data",
"output_schema": map[string]any{"type": "object", "properties": map[string]any{"title": map[string]string{"type": "string"}}},
"execution_type": "single",
})
cr, _ := http.NewRequest("POST", base+"/api/v1/tasks", bytes.NewReader(createBody))
cr.Header.Set("Authorization", "Bearer "+tok)
cr.Header.Set("Content-Type", "application/json")
crResp, _ := http.DefaultClient.Do(cr)
crBytes, _ := io.ReadAll(crResp.Body)
crResp.Body.Close()
var createEnv struct{ Data struct{ ID string `json:"id"` } `json:"data"` }
json.Unmarshal(crBytes, &createEnv)
taskID := createEnv.Data.ID
rr, _ := http.NewRequest("POST", base+"/api/v1/tasks/"+taskID+"/run", nil)
rr.Header.Set("Authorization", "Bearer "+tok)
runResp, _ := http.DefaultClient.Do(rr)
runBytes, _ := io.ReadAll(runResp.Body)
runResp.Body.Close()
var runEnv struct {
Data struct {
ID string `json:"id"`
Status string `json:"status"`
} `json:"data"`
}
json.Unmarshal(runBytes, &runEnv)
execID, status := runEnv.Data.ID, runEnv.Data.Status
for status == "queued" || status == "running" || status == "paused" {
time.Sleep(3 * time.Second)
pr, _ := http.NewRequest("GET", base+"/api/v1/executions/"+execID, nil)
pr.Header.Set("Authorization", "Bearer "+tok)
pResp, _ := http.DefaultClient.Do(pr)
pBytes, _ := io.ReadAll(pResp.Body)
pResp.Body.Close()
json.Unmarshal(pBytes, &runEnv)
status = runEnv.Data.Status
}
er, _ := http.NewRequest("GET", base+"/api/v1/executions/"+execID+"/results/export", nil)
er.Header.Set("Authorization", "Bearer "+tok)
eResp, _ := http.DefaultClient.Do(er)
exportBytes, _ := io.ReadAll(eResp.Body)
eResp.Body.Close()
os.WriteFile("results.json", exportBytes, 0o644)
fmt.Println("done", taskID, execID, status)
const base = process.env.TITAN_API_URL!;
const token = process.env.TITAN_TOKEN!;
// Assumes titanRequest / titanJson from "Build a minimal client" (same names).
const task = await titanRequest<{ id: string }>("POST", "/tasks", {
name: "My task",
urls: ["https://example.com"],
objective: "Extract data",
output_schema: { type: "object", properties: { title: { type: "string" } } },
execution_type: "single",
});
const runRes = await fetch(`${base}/api/v1/tasks/${task.id}/run`, {
method: "POST",
headers: { Authorization: `Bearer ${token}` },
});
const run = await titanJson<{ id: string; status: string }>(runRes);
let exec = run;
while (["queued", "running", "paused"].includes(exec.status)) {
await new Promise((r) => setTimeout(r, 3000));
exec = await titanRequest<{ id: string; status: string }>(
"GET",
`/executions/${run.id}`,
);
}
const exportRes = await fetch(`${base}/api/v1/executions/${run.id}/results/export`, {
headers: { Authorization: `Bearer ${token}` },
});
if (!exportRes.ok) throw new Error(await exportRes.text());
const results = JSON.parse(await exportRes.text());
console.log(results);
# Assumes titan_request from "Build a minimal client".
task = titan_request(
"POST",
"/tasks",
{
"name": "My task",
"urls": ["https://example.com"],
"objective": "Extract data",
"output_schema": {
"type": "object",
"properties": {"title": {"type": "string"}},
},
"execution_type": "single",
},
)
task_id = task["id"]
import json
import os
import time
import urllib.request
base = os.environ["TITAN_API_URL"]
tok = os.environ["TITAN_TOKEN"]
req = urllib.request.Request(
f"{base}/api/v1/tasks/{task_id}/run",
headers={"Authorization": f"Bearer {tok}"},
method="POST",
)
with urllib.request.urlopen(req) as resp:
run = json.loads(resp.read().decode())["data"]
exec_id, status = run["id"], run["status"]
while status in ("queued", "running", "paused"):
time.sleep(3)
req = urllib.request.Request(
f"{base}/api/v1/executions/{exec_id}",
headers={"Authorization": f"Bearer {tok}"},
method="GET",
)
with urllib.request.urlopen(req) as resp:
status = json.loads(resp.read().decode())["data"]["status"]
req = urllib.request.Request(
f"{base}/api/v1/executions/{exec_id}/results/export",
headers={"Authorization": f"Bearer {tok}"},
method="GET",
)
with urllib.request.urlopen(req) as resp:
results = json.loads(resp.read().decode())
print(results)
use serde_json::json;
let base = std::env::var("TITAN_API_URL").expect("TITAN_API_URL");
let token = std::env::var("TITAN_TOKEN").expect("TITAN_TOKEN");
let task: serde_json::Value = {
let body = json!({
"name": "My task",
"urls": ["https://example.com"],
"objective": "Extract data",
"output_schema": { "type": "object", "properties": { "title": { "type": "string" } } },
"execution_type": "single"
});
let resp = ureq::post(format!("{base}/api/v1/tasks"))
.set("Authorization", &format!("Bearer {token}"))
.set("Content-Type", "application/json")
.send_json(body).expect("http");
let v: serde_json::Value = serde_json::from_str(&resp.into_string().expect("body")).expect("json");
v["data"].clone()
};
let task_id = task["id"].as_str().unwrap();
let run: serde_json::Value = {
let resp = ureq::post(format!("{base}/api/v1/tasks/{task_id}/run"))
.set("Authorization", &format!("Bearer {token}"))
.send_json(json!({})).expect("http");
let v: serde_json::Value = serde_json::from_str(&resp.into_string().expect("body")).expect("json");
v["data"].clone()
};
let exec_id = run["id"].as_str().unwrap().to_string();
let mut status = run["status"].as_str().unwrap().to_string();
while matches!(status.as_str(), "queued" | "running" | "paused") {
std::thread::sleep(std::time::Duration::from_secs(3));
let resp = ureq::get(format!("{base}/api/v1/executions/{exec_id}"))
.set("Authorization", &format!("Bearer {token}"))
.call().expect("http");
let v: serde_json::Value = serde_json::from_str(&resp.into_string().expect("body")).expect("json");
status = v["data"]["status"].as_str().unwrap().to_string();
}
let resp = ureq::get(format!("{base}/api/v1/executions/{exec_id}/results/export"))
.set("Authorization", &format!("Bearer {token}"))
.call().expect("http");
let results: serde_json::Value = serde_json::from_str(&resp.into_string().expect("body")).expect("json");
println!("{results}");
Ok::<(), Box<dyn std::error::Error>>(())
Best practices
| Practice | Why it matters |
|---|---|
| Keep one reusable task per workflow | Produces cleaner execution history |
| Parse the response envelope consistently | Avoids ad hoc response handling |
| Poll executions, then switch to result endpoints | Keeps runtime and result logic separate |
| Use API keys for automation | Avoids browser-session coupling |
| Keep output schemas stable | Makes downstream consumption simpler |
Troubleshooting
| Problem | What to check |
|---|---|
Task creation returns 400 | Invalid body or output schema |
Execution never moves past queued | Worker availability or scheduler health |
| Export endpoints fail | Execution may not be completed yet |
| Media does not render in frontend | Use the correct export mode or media download path |
Next steps
- Create and manage tasks
- Monitor and control executions
- Download results and access media
- HTTP errors and exceptions — non-standard error bodies and rate limits