API Reference
Base URL
https://perceptron.cloud/api/
Authentication
Perceptron uses two token types. Include one as a bearer token on every protected request.
Authorization: Bearer YOUR_TOKEN
Master Key: a pct-m-<64_hex>
string (70 chars) obtained by signing a wallet challenge via
POST /api/auth/verify. Required for key management,
metrics, and master-key-only operations. Treat it like a root
credential.
Client Key: a pct-<64_hex> string
(68 chars) generated via POST /api/keys/generate (requires
master key). Use this for inference, credits, feeds, domains, and
points. Safe to hand to an agent.
Monetary convention: monetary amounts are
dollar strings (e.g. "25" = $25.00). 1
credit = $1.
Health
GET /api/health No Auth
Returns server status and current version. Useful for liveness checks.
{
"status": "ok",
"version": "3.2.23"
}curl https://perceptron.cloud/api/healthimport requests
response = requests.get("https://perceptron.cloud/api/health")
print(response.json())Version
Release information endpoints. No authentication required.
GET /api/version/latest No Auth
Returns the latest release record, determined by the highest semantic version.
{
"version": "3.2.23",
"release_notes": "## 3.2.23\n\n- Added version check and update modal\n- Fixed tunnel reconnect backoff\n- Improved error handling in domain proxy",
"downloads": [
"https://perceptron.cloud/static/release/3.2.23/linux/perceptron.tar.gz"
],
"published_at": 1748000000
}| Field | Type | Description |
|---|---|---|
| version | string |
Semver version string (e.g. “3.2.23”)
|
| release_notes | string | Raw markdown release notes |
| downloads | array | Platform-specific tarball download URLs |
| published_at | uint64 | Unix timestamp (seconds) of publication |
curl https://perceptron.cloud/api/version/latestimport requests
response = requests.get("https://perceptron.cloud/api/version/latest")
print(response.json()["version"])GET /api/version/{version} No Auth
Returns a specific release record by semver string.
| Parameter | Type | Description |
|---|---|---|
| version | string |
Semver version string, e.g. 3.2.23
|
Same shape as GET /api/version/latest.
curl https://perceptron.cloud/api/version/3.2.23import requests
response = requests.get("https://perceptron.cloud/api/version/3.2.23")
print(response.json()["release_notes"])Authentication Endpoints
GET /api/auth/challenge No Auth
Generates a one-time nonce that must be signed by your EVM wallet.
The nonce expires after 2 hours. Pass it to
POST /api/auth/verify.
{
"nonce": "a3f2e1d0-...",
"expires_at": 1748000300
}| Field | Type | Description |
|---|---|---|
| nonce | string | UUID to sign with your wallet’s private key |
| expires_at | uint64 | Unix timestamp after which the nonce is invalid |
curl https://perceptron.cloud/api/auth/challengeimport requests
response = requests.get("https://perceptron.cloud/api/auth/challenge")
print(response.json())
# {"nonce": "a3f2e1d0-...", "expires_at": 1748000300}POST /api/auth/verify No Auth
Signs a challenge nonce with an EVM wallet
(personal_sign, secp256k1) and returns a master key. The
master key acts as your bearer token for all subsequent privileged
calls. Store it securely. It is not recoverable, but you can regenerate
it.
| Field | Type | Required | Description |
|---|---|---|---|
| address | string | yes |
EVM wallet address (0x…)
|
| signature | string | yes |
65-byte secp256k1 personal_sign signature as a hex string
|
| nonce | string | yes |
Nonce UUID from GET /api/auth/challenge
|
{
"master_key": "pct-m-a1b2c3d4e5f6...",
"address": "0xabcdef..."
}| Field | Type | Description |
|---|---|---|
| master_key | string |
pct-m- prefixed bearer token (70 chars). Use as
Authorization: Bearer <master_key>
|
| address | string | Lowercase recovered wallet address |
POST /api/auth/regenerate-master-key Master Key Only
Revokes the current master key and issues a new one. All existing
client API keys (pct-...) remain valid. No request body
required.
{
"master_key": "pct-m-f9e8d7c6b5a4...",
"address": "0xabcdef..."
}Chat Completions
POST /api/v1/chat/completions · /api/chat/completions Client or Master Key
OpenAI-compatible chat completions. Both paths behave identically. Supports streaming via SSE. Billed per token at per-model rates, deducted from your credit balance.
| Field | Type | Required | Description |
|---|---|---|---|
| model | string | yes |
Model ID from GET /api/models
|
| messages | array | yes | Array of message objects, see below |
| messages[].role | string | yes |
“system”, “user”, “assistant”, or
“tool”
|
| messages[].content | string | yes | Message text |
| stream | bool | no |
Default false. Set true for SSE streaming
|
| temperature | float | no |
Default 0.7
|
| top_p | float | no |
Default 0.9
|
| max_tokens | uint64 | no |
Clamped to the model’s max_output_tokens and available
context
|
| max_completion_tokens | uint64 | no |
Alias for max_tokens, same clamping logic
|
| frequency_penalty | float | no |
Default 0.1
|
| presence_penalty | float | no | Passed to the model if provided |
| top_k | any | no | Passed to the model if provided |
| stop | any | no | Passed to the model if provided |
| seed | any | no | Passed to the model if provided |
| n | any | no | Passed to the model if provided |
| tools | array | no | Tool definitions. Only accepted if the model supports function calling |
| tool_choice | any | no | Only accepted if the model supports function calling |
| miners | boolean | no | Route the request through Perceptron’s peer compute network |
| miners_no_fallback | boolean | no | Fail rather than fall back to the cloud when no miner serves the request |
| use_my_nodes | boolean | no | Run the request on your own registered nodes |
| node | string | no |
Pin a single node by its public node_id from GET
/api/mining/nodes
|
| miner | string | no |
Pin a miner by its public miner_id (any of that account’s
nodes may serve)
|
| node_blacklist | array | no |
Public node_id values to exclude from routing
|
| miner_blacklist | array | no |
Public miner_id values to exclude from routing
|
{
"id": "chatcmpl-abc123",
"object": "chat.completion",
"created": 1748000000,
"model": "bartmoss/deimos-3.5-pro",
"choices": [{
"index": 0,
"message": {
"role": "assistant",
"content": "The answer is 42."
},
"finish_reason": "stop"
}],
"usage": {
"prompt_tokens": 24,
"completion_tokens": 8,
"total_tokens": 32
}
}The usage object is constructed by Perceptron from
scratch; no upstream cost or provider metadata leaks through. When the
upstream provider reports prompt-cache activity,
usage.prompt_tokens_details is included:
"usage": {
"prompt_tokens": 10339,
"completion_tokens": 60,
"total_tokens": 10399,
"prompt_tokens_details": {
"cached_tokens": 10318,
"cache_write_tokens": 0
}
}cached_tokens are billed at the model’s
pricing.chat.cache_read_per_1m rate (typically 0.1× input);
cache_write_tokens are billed at
pricing.chat.cache_write_per_1m (typically 1.25× input).
When either rate is absent from GET /api/models, the full
pricing.chat.input_per_1m rate applies to all prompt
tokens. Request bodies may carry an optional quantization
field selecting one of the model’s declared weight quants (validated
against them; absent = any variant).
Returns Content-Type: text/event-stream. Each event is a
chat.completion.chunk:
data: {"id":"chatcmpl-abc123","object":"chat.completion.chunk","created":1748000000,"model":"bartmoss/deimos-3.5-pro","choices":[{"index":0,"delta":{"role":"assistant","content":"The "},"finish_reason":null}]}
data: {"id":"chatcmpl-abc123","choices":[{"index":0,"delta":{"content":"answer is 42."},"finish_reason":null}]}
data: {"id":"chatcmpl-abc123","choices":[{"index":0,"delta":{},"finish_reason":"stop"}],"usage":{"prompt_tokens":24,"completion_tokens":8,"total_tokens":32,"prompt_tokens_details":{"cached_tokens":0,"cache_write_tokens":0}}}
data: [DONE]
If credits run out mid-stream:
data: {"error":{"message":"credit limit reached","type":"insufficient_credits"},"credits_exhausted":true}
data: [DONE]
curl https://perceptron.cloud/api/v1/chat/completions \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "bartmoss/deimos-3.5-pro",
"messages": [
{"role": "user", "content": "What is the capital of France?"}
],
"stream": false
}'import requests, json
response = requests.post(
"https://perceptron.cloud/api/v1/chat/completions",
headers={"Authorization": "Bearer YOUR_API_KEY"},
json={
"model": "bartmoss/deimos-3.5-pro",
"messages": [{"role": "user", "content": "What is the capital of France?"}],
"stream": False,
},
)
print(json.dumps(response.json(), indent=2))GET /api/models No Auth
Returns all active models with pricing and capability metadata.
{
"object": "list",
"data": [
{
"id": "bartmoss/deimos-3.5-pro",
"name": "Kimi K2.6",
"object": "model",
"created": 1748000000,
"owned_by": "perceptron",
"context_length": 262144,
"max_output_tokens": 183500,
"frontier_rank": 7,
"pricing": {
"chat": {
"input_per_1m": "x",
"output_per_1m": "x"
}
}
}
]
}| Field | Type | Description |
|---|---|---|
| id | string | Model ID to use in chat requests |
| context_length | uint32 | Maximum total tokens (input + output) |
| max_output_tokens | uint32 | Maximum output tokens per request |
| frontier_rank | uint32 | Capability ranking (lower = more capable) |
| pricing.chat.input_per_1m | string | Cost per 1 million input tokens |
| pricing.chat.output_per_1m | string | Cost per 1 million output tokens |
curl https://perceptron.cloud/api/modelsimport requests
response = requests.get("https://perceptron.cloud/api/models")
for model in response.json()["data"]:
print(model["id"], model["pricing"])Speech
Transcribe Audio (Speech-to-Text)
POST /api/v1/audio/transcriptions · /api/stt/transcriptions Client or Master Key
Transcribe an audio file to text. OpenAI-compatible
multipart/form-data upload. Billed per minute of audio.
Maximum 50 MB per file.
| Part | Type | Required | Description |
|---|---|---|---|
| file | file | yes | The audio file (mp3, wav, ogg, m4a, flac, webm, etc.) |
| model | string | no |
STT model ID from GET /api/stt/models
|
| language | string | no |
ISO-639-1 language hint (e.g. en)
|
| response_format | string | no |
Response format (default verbose_json)
|
| miners | boolean | no | Route the request through Perceptron’s peer compute network |
| use_my_nodes | boolean | no | Run the request on your own registered nodes |
| node | string | no |
Pin a single node by its public node_id from GET
/api/mining/nodes
|
| miner | string | no |
Pin a miner by its public miner_id (any of that account’s
nodes may serve)
|
| node_blacklist | array | no |
Public node_id values to exclude from routing
|
| miner_blacklist | array | no |
Public miner_id values to exclude from routing
|
{
"text": "Hello, this is the transcription.",
"duration": 3.2
}curl https://perceptron.cloud/api/v1/audio/transcriptions \
-H "Authorization: Bearer $PERCEPTRON_API_KEY" \
-F "model=openai/whisper-large-v3" \
-F "file=@speech.ogg"List STT Models
GET /api/stt/models No Auth
Returns all speech-to-text models with their per-hour audio pricing.
{
"object": "list",
"data": [
{
"id": "openai/whisper-large-v3",
"name": "Whisper Large v3",
"object": "model",
"owned_by": "perceptron",
"pricing": { "stt": { "per_hour": "x" } }
}
]
}Synthesize Speech (Text-to-Speech)
POST /api/v1/audio/speech · /api/tts/speech Client or Master Key
Synthesize speech from text. Returns raw audio bytes. Billed per input character.
| Field | Type | Required | Description |
|---|---|---|---|
| model | string | yes |
TTS model ID from GET /api/tts/models
|
| input | string | yes | The text to synthesize |
| voice | string | no |
Voice id (provider-specific, e.g. af_heart)
|
| response_format | string | no |
Audio format: mp3 (default), wav,
opus, ogg, flac
|
| miners | boolean | no | Route the request through Perceptron’s peer compute network |
| use_my_nodes | boolean | no | Run the request on your own registered nodes |
| node | string | no |
Pin a single node by its public node_id from GET
/api/mining/nodes
|
| miner | string | no |
Pin a miner by its public miner_id (any of that account’s
nodes may serve)
|
| node_blacklist | array | no |
Public node_id values to exclude from routing
|
| miner_blacklist | array | no |
Public miner_id values to exclude from routing
|
Raw audio bytes with Content-Type: audio/mpeg (or
audio/wav, audio/ogg per
response_format).
curl https://perceptron.cloud/api/v1/audio/speech \
-H "Authorization: Bearer $PERCEPTRON_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model": "hexgrad/kokoro-82m", "input": "Hello, world.", "voice": "af_heart", "response_format": "mp3"}' \
--output speech.mp3List TTS Models
GET /api/tts/models No Auth
Returns all text-to-speech models with their per-character pricing.
{
"object": "list",
"data": [
{
"id": "hexgrad/kokoro-82m",
"name": "Kokoro 82M",
"object": "model",
"owned_by": "perceptron",
"pricing": { "tts": { "per_1m_chars": "x" } },
"context_length": 4000
}
]
}Video
Video generation is asynchronous: submit a job, poll its status, download the result. Billed per output second at the model’s rate for the requested resolution; the full quoted cost is reserved at submit and refunded on failure. Finished videos are stored as artifacts and accrue hourly artifact storage charges until deleted.
Submit Video Job
POST /api/v1/videos Client or Master Key
Submit a video generation job. Returns 202 Accepted with
the job id and polling URL.
| Field | Type | Required | Description |
|---|---|---|---|
| model | string | yes |
Video model ID from GET /api/v1/videos/models
|
| prompt | string | yes | Text description of the video to generate |
| duration | integer | yes | Clip duration in seconds (per-model min/max) |
| resolution | string | no |
Output resolution tier (e.g. 2K); defaults to the model’s
first supported tier
|
| aspect_ratio | string | no |
Aspect ratio (e.g. 16:9, 9:16,
1:1)
|
| size | string | no |
Exact pixel dimensions in WIDTHxHEIGHT format
|
| generate_audio | boolean | no | Generate audio alongside the video (models with audio support) |
| seed | integer | no | Seed for deterministic generation (not guaranteed) |
| frame_images | array | no |
First/last frame images for image-to-video (frame_type:
first_frame / last_frame)
|
| input_references | array | no | Reference images for style guidance (reference-to-video) |
| miners | boolean | no | Route the request through Perceptron’s peer compute network |
| use_my_nodes | boolean | no | Run the request on your own registered nodes |
| node | string | no |
Pin a single node by its public node_id from GET
/api/mining/nodes
|
| miner | string | no |
Pin a miner by its public miner_id (any of that account’s
nodes may serve)
|
| node_blacklist | array | no |
Public node_id values to exclude from routing
|
| miner_blacklist | array | no |
Public miner_id values to exclude from routing
|
callback_url and provider are not supported
and are rejected with 400.
{
"id": "0e1b0c3f-...",
"polling_url": "/api/v1/videos/0e1b0c3f-...",
"status": "pending"
}curl -X POST https://perceptron.cloud/api/v1/videos \
-H "Authorization: Bearer $PERCEPTRON_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model": "minimax/hailuo-3", "prompt": "A golden retriever playing fetch on a sunny beach", "duration": 5, "resolution": "2K", "aspect_ratio": "16:9"}'Poll Video Job
GET /api/v1/videos/{id} Client or Master Key
Poll a job’s status. Statuses: pending,
in_progress, completed, failed,
cancelled, expired. Jobs in terminal failure
statuses are fully refunded. Completed jobs include
unsigned_urls and usage.cost (USD).
{
"id": "0e1b0c3f-...",
"polling_url": "/api/v1/videos/0e1b0c3f-...",
"status": "completed",
"unsigned_urls": ["/api/v1/videos/0e1b0c3f-.../content?index=0"],
"usage": { "cost": 1.5 }
}curl https://perceptron.cloud/api/v1/videos/0e1b0c3f-... \
-H "Authorization: Bearer $PERCEPTRON_API_KEY"Download Video Content
GET /api/v1/videos/{id}/content Client or Master Key
Download the generated video. Raw video bytes with
Content-Type: video/mp4, served from the locally stored
artifact. Accepts an optional index query parameter
(default 0).
curl https://perceptron.cloud/api/v1/videos/0e1b0c3f-.../content \
-H "Authorization: Bearer $PERCEPTRON_API_KEY" \
--output video.mp4List Video Jobs
GET /api/v1/videos Client or Master Key
List the authenticated user’s video generation jobs, newest first.
{
"jobs": [
{
"id": "0e1b0c3f-...",
"polling_url": "/api/v1/videos/0e1b0c3f-...",
"status": "completed",
"model": "minimax/hailuo-3",
"prompt": "A golden retriever playing fetch on a sunny beach",
"duration": 5,
"resolution": "2K",
"aspect_ratio": "16:9",
"generate_audio": null,
"created_at": 1785000000,
"completed_at": 1785000180,
"unsigned_urls": ["/api/v1/videos/0e1b0c3f-.../content?index=0"],
"usage": { "cost": 1.5 },
"artifact_id": "b7f2..."
}
]
}Delete Video Job
DELETE /api/v1/videos/{id} Client or Master Key
Delete a video generation job and its stored artifact, stopping artifact storage billing for those bytes.
curl -X DELETE https://perceptron.cloud/api/v1/videos/0e1b0c3f-... \
-H "Authorization: Bearer $PERCEPTRON_API_KEY"List Video Models
GET /api/v1/videos/models No Auth
Returns all video generation models with their capabilities and per-second pricing per resolution tier.
{
"object": "list",
"data": [
{
"id": "minimax/hailuo-3",
"name": "Hailuo 3",
"object": "model",
"owned_by": "perceptron",
"input_modalities": ["text", "image"],
"output_modalities": ["video"],
"supported_resolutions": ["2K"],
"supported_aspect_ratios": ["21:9", "16:9", "4:3", "1:1", "3:4", "9:16"],
"min_duration": 4,
"max_duration": 15,
"supports_audio": true,
"pricing_skus": { "2K": "x" }
}
]
}Image
Image generation is asynchronous (submit → poll → download). Billed
per output image at the model’s rate for the requested resolution; the
full quoted cost (n × rate) is reserved at submit and
refunded on failure. Finished images are stored as artifacts and accrue
hourly artifact storage charges until deleted. See Billing.
Submit Image Job
POST /api/v1/images Client or Master Key
Submit an image generation job. Returns 202 with the job
id and a polling URL.
curl -X POST https://perceptron.cloud/api/v1/images \
-H "Authorization: Bearer $PERCEPTRON_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "krea/krea-2-large",
"prompt": "A neon-lit cyberpunk street market in the rain",
"resolution": "1K",
"aspect_ratio": "16:9",
"n": 1,
"seed": 42
}'| Field | Type | Required | Description |
|---|---|---|---|
model |
string | yes | Image model ID |
prompt |
string | yes | Text description of the image to generate |
resolution |
string | no | Output resolution tier; defaults to the model’s first supported tier |
aspect_ratio |
string | no | Aspect ratio (e.g. 16:9, 9:16,
1:1) |
n |
integer | no | Number of images to generate (1..max_n, default
1) |
seed |
integer | no | Seed for deterministic generation (models with seed support) |
quality |
string | no | auto, low, medium, or
high |
output_format |
string | no | png, jpeg, or webp |
background |
string | no | auto, transparent, or
opaque |
input_references |
array | no | Reference images for image-to-image generation (up to 1) |
miners |
boolean | no | Route the request through Perceptron’s peer compute network |
use_my_nodes |
boolean | no | Run the request on your own registered nodes |
node |
string | no | Pin a single node by its public node_id from
GET /api/mining/nodes |
miner |
string | no | Pin a miner by its public miner_id (any of that
account’s nodes may serve) |
node_blacklist |
array | no | Public node_id values to exclude from routing |
miner_blacklist |
array | no | Public miner_id values to exclude from routing |
output_compression, stream,
size, callback_url, and provider
are not supported; rejected with 400. Insufficient credits
return 402.
Response (202)
{
"id": "0e1b0c3f-...",
"polling_url": "/api/v1/images/0e1b0c3f-...",
"status": "pending"
}Example
curl -X POST https://perceptron.cloud/api/v1/images \
-H "Authorization: Bearer $PERCEPTRON_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model":"krea/krea-2-large","prompt":"a red panda astronaut","resolution":"1K"}'Poll Image Job
GET /api/v1/images/{id} Client or Master Key
Poll an image generation job’s status. Statuses:
pending, in_progress, completed,
failed, cancelled. Completed jobs include
unsigned_urls (one per n),
artifact_ids, and usage.cost (USD).
{
"id": "0e1b0c3f-...",
"status": "completed",
"model": "krea/krea-2-large",
"prompt": "a red panda astronaut",
"n": 1,
"unsigned_urls": ["/api/v1/images/0e1b0c3f-.../content?index=0"],
"usage": { "cost": 0.30 }
}curl https://perceptron.cloud/api/v1/images/0e1b0c3f-... \
-H "Authorization: Bearer $PERCEPTRON_API_KEY"Download Image Content
GET /api/v1/images/{id}/content?index=N Client or Master Key
Download one generated image (raw bytes). index is
0-based (use 0..n-1).
Content-Type reflects the output format
(e.g. image/png).
curl https://perceptron.cloud/api/v1/images/0e1b0c3f-.../content?index=0 \
-H "Authorization: Bearer $PERCEPTRON_API_KEY" --output image.pngList Image Jobs
GET /api/v1/images Client or Master Key
List the caller’s image generation jobs, newest first.
{
"jobs": [
{
"id": "0e1b0c3f-...",
"status": "completed",
"model": "krea/krea-2-large",
"prompt": "a red panda astronaut",
"n": 1,
"usage": { "cost": 0.30 }
}
]
}curl https://perceptron.cloud/api/v1/images \
-H "Authorization: Bearer $PERCEPTRON_API_KEY"Delete Image Job
DELETE /api/v1/images/{id} Client or Master Key
Delete an image generation job and its stored artifacts, stopping artifact storage billing for those bytes.
curl -X DELETE https://perceptron.cloud/api/v1/images/0e1b0c3f-... \
-H "Authorization: Bearer $PERCEPTRON_API_KEY"List Image Models
GET /api/v1/images/models No Auth
Returns all image generation models with their capabilities and per-image pricing per resolution tier.
{
"object": "list",
"data": [
{
"id": "krea/krea-2-large",
"name": "Krea 2 Large",
"object": "model",
"owned_by": "perceptron",
"input_modalities": ["text", "image"],
"output_modalities": ["image"],
"supported_resolutions": ["1K"],
"supported_aspect_ratios": ["1:1", "4:3", "3:2", "16:9", "4:5", "2:3", "9:16"],
"supports_seed": true,
"max_n": 10,
"pricing_skus": { "1K": "x" }
}
]
}Vision
Video (and optionally image) understanding with text output:
descriptions, analysis, or Q&A. Billed per token (input + output) at
the model’s per-million rates, the same as chat. Recorded as
VisionInference transactions. See Billing.
Analyze Video/Image
POST /api/v1/vision Client or Master Key
Analyze a video or image with a text prompt and return text output.
multipart/form-data upload. Supported video formats: mp4,
webm, mov, mkv.
| Part | Type | Required | Description |
|---|---|---|---|
| file | file | no | The video or image file (mp4, webm, mov, mkv, png, jpg, gif, webp). At least one media input is required. |
| image_file | file | no | Additional image (repeatable for multiple images) |
| prompt | string | yes | Text prompt (description, question, or instruction) |
| model | string | no |
Vision model ID from GET /api/v1/vision/models
|
| artifact_id | string | no |
Artifact reference (existing artifact id, alternative to
file)
|
| image_artifact_id | string | no |
Image artifact reference (repeatable for multiple; alternative to
image_file)
|
| max_tokens | uint64 | no | Maximum output tokens |
| miners | boolean | no | Route the request through Perceptron’s peer compute network |
| use_my_nodes | boolean | no | Run the request on your own registered nodes |
| node | string | no |
Pin a single node by its public node_id from GET
/api/mining/nodes
|
| miner | string | no |
Pin a miner by its public miner_id (any of that account’s
nodes may serve)
|
| node_blacklist | array | no |
Public node_id values to exclude from routing
|
| miner_blacklist | array | no |
Public miner_id values to exclude from routing
|
{
"text": "A dog runs across a sunny beach, chasing a thrown ball.",
"usage": {
"prompt_tokens": 24,
"completion_tokens": 16,
"total_tokens": 40
}
}curl https://perceptron.cloud/api/v1/vision \
-H "Authorization: Bearer $PERCEPTRON_API_KEY" \
-F "model=minimax/m3" \
-F "prompt=Describe what is happening in this video." \
-F "file=@clip.mp4"List Vision Models
GET /api/v1/vision/models No Auth
Returns all vision models, those with "video" in their
input_modalities, with their per-million token pricing.
{
"object": "list",
"data": [
{
"id": "minimax/m3",
"name": "MiniMax M3",
"object": "model",
"owned_by": "perceptron",
"input_modalities": ["video", "image"],
"pricing": {
"chat": {
"input_per_1m": "x",
"output_per_1m": "x"
}
}
}
]
}curl https://perceptron.cloud/api/v1/vision/modelsMusic
Music generation is asynchronous: submit a job, poll its status,
download the result. Billed per output second at the model’s rate; the
full quoted cost (duration × rate × n) is reserved at
submit and refunded on failure. Finished tracks are stored as artifacts
and accrue hourly artifact storage charges until deleted. See Billing.
Submit Music Job
POST /api/v1/music Client or Master Key
Submit a music generation job. Returns 202 Accepted with
the job id and polling URL.
| Field | Type | Required | Description |
|---|---|---|---|
| model | string | yes |
Music model ID from GET /api/v1/music/models
|
| prompt | string | yes | Style/caption description of the music (genre, mood, instrumentation) |
| task_type | string | no |
Job kind: text2music (default), cover,
repaint, lego, extract,
complete
|
| duration | integer | no |
Target duration in seconds (per-model min/max; default 180)
|
| n | integer | no |
Number of tracks to generate (1..max_n,
default 1)
|
| lyrics | string | no | Lyrics for a sung song; omit for instrumental music |
| vocal_language | string | no |
Language code for the vocals (e.g. en, zh,
ja)
|
| bpm | integer | no | Tempo in beats per minute |
| key_scale | string | no |
Musical key (e.g. C major, A minor)
|
| time_signature | string | no |
Time signature (e.g. 4)
|
| format | string | no |
Output audio format: mp3 (default), wav,
flac, opus, or aac
|
| seed | integer | no | Seed for deterministic generation |
| thinking | boolean | no |
Let the model plan the arrangement before rendering (default
true)
|
| use_format | boolean | no |
Treat prompt as a structured style/caption format (default
false)
|
| reference_artifact_id | string | no |
Artifact ID of a reference audio file in your account (for
cover and style transfer)
|
| src_artifact_id | string | no |
Artifact ID of the source audio to edit (for repaint,
lego, extract, complete)
|
| miners | boolean | no | Route the request through Perceptron’s peer compute network |
| use_my_nodes | boolean | no | Run the request on your own registered nodes |
| node | string | no |
Pin a single node by its public node_id from GET
/api/mining/nodes
|
| miner | string | no |
Pin a miner by its public miner_id (any of that account’s
nodes may serve)
|
| node_blacklist | array | no |
Public node_id values to exclude from routing
|
| miner_blacklist | array | no |
Public miner_id values to exclude from routing
|
Reference and source audio are supplied as artifact IDs from your
account, not inline bytes. Audio-input task types require the model to
declare supports_reference_audio.
{
"id": "0e1b0c3f-...",
"polling_url": "/api/v1/music/0e1b0c3f-...",
"status": "pending"
}curl -X POST https://perceptron.cloud/api/v1/music \
-H "Authorization: Bearer $PERCEPTRON_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model": "ace-step/ace-step-v1.5-xl", "prompt": "warm indie folk, fingerpicked guitar, brushed drums", "lyrics": "[Verse] Morning light across the floor\n[Chorus] We are on our way", "duration": 120, "format": "mp3"}'Poll Music Job
GET /api/v1/music/{id} Client or Master Key
Poll a job’s status. Statuses: pending,
in_progress, completed, failed,
cancelled, expired. Jobs in terminal failure
statuses are fully refunded. Completed jobs include
unsigned_urls and usage.cost (USD). When
n > 1, unsigned_urls contains one entry per
track.
{
"id": "0e1b0c3f-...",
"polling_url": "/api/v1/music/0e1b0c3f-...",
"status": "completed",
"unsigned_urls": ["/api/v1/music/0e1b0c3f-.../content?index=0"],
"usage": { "cost": 0.08 }
}curl https://perceptron.cloud/api/v1/music/0e1b0c3f-... \
-H "Authorization: Bearer $PERCEPTRON_API_KEY"Download Music Content
GET /api/v1/music/{id}/content?index=N Client or Master Key
Download a generated track. Raw audio bytes with a
Content-Type matching the requested format
(e.g. audio/mpeg, audio/wav), served from the
locally stored artifact. index is 0-based and defaults to
0.
curl https://perceptron.cloud/api/v1/music/0e1b0c3f-.../content?index=0 \
-H "Authorization: Bearer $PERCEPTRON_API_KEY" \
--output track.mp3List Music Jobs
GET /api/v1/music Client or Master Key
List the authenticated user’s music generation jobs, newest first.
{
"jobs": [
{
"id": "0e1b0c3f-...",
"polling_url": "/api/v1/music/0e1b0c3f-...",
"status": "completed",
"model": "ace-step/ace-step-v1.5-xl",
"task_type": "text2music",
"prompt": "warm indie folk, fingerpicked guitar, brushed drums",
"lyrics": "[Verse] Morning light across the floor\n[Chorus] We are on our way",
"duration": 120,
"n": 1,
"format": "mp3",
"created_at": 1785000000,
"completed_at": 1785000120,
"unsigned_urls": ["/api/v1/music/0e1b0c3f-.../content?index=0"],
"artifact_ids": ["b7f2..."],
"usage": { "cost": 0.08 }
}
]
}Delete Music Job
DELETE /api/v1/music/{id} Client or Master Key
Delete a music generation job and its stored tracks, stopping artifact storage billing for those bytes. In-flight jobs are cancelled and refunded.
curl -X DELETE https://perceptron.cloud/api/v1/music/0e1b0c3f-... \
-H "Authorization: Bearer $PERCEPTRON_API_KEY"List Music Models
GET /api/v1/music/models No Auth
Returns all music generation models with their capabilities and per-second pricing.
{
"object": "list",
"data": [
{
"id": "ace-step/ace-step-v1.5-xl",
"name": "ACE-Step 1.5 XL",
"object": "model",
"owned_by": "perceptron",
"input_modalities": ["text", "audio"],
"output_modalities": ["music"],
"min_duration": 10,
"max_duration": 600,
"supports_lyrics": true,
"supports_instrumental": true,
"supports_reference_audio": true,
"max_n": 8,
"task_types": ["text2music", "cover", "repaint", "lego", "extract", "complete"],
"pricing": { "music": { "per_second": "x" } },
"is_free": false
}
]
}curl https://perceptron.cloud/api/v1/music/modelsDecision
Typed decisions with IO-family models: provide a rendered observation
and a set of typed questions, and receive one probability distribution
per question. Decision models return distributions, not text. Billed per
token (input + output) at the model’s per-million decision rates.
Recorded as DecisionInference transactions. See Billing.
Decide
POST /api/v1/decide Client or Master Key
Answer a rendered observation with one or more typed questions.
application/json body. The response contains one answer per
question.
| Field | Type | Required | Description |
|---|---|---|---|
| model | string | no |
Decision model ID from GET /api/v1/decide/models; omit to
use the default decision model
|
| state | string | yes | Rendered observation text the model decides over |
| questions | array | yes |
Typed question objects: id, kind,
instructions, and options
|
| seed | uint64 | no | Sampling seed; a fixed seed reproduces the same answers |
| miners | boolean | no | Route the request through Perceptron’s peer compute network |
| use_my_nodes | boolean | no | Run the request on your own registered nodes |
| node | string | no |
Pin a single node by its public node_id from GET
/api/mining/nodes
|
| miner | string | no |
Pin a miner by its public miner_id (any of that account’s
nodes may serve)
|
| node_blacklist | array | no |
Public node_id values to exclude from routing
|
| miner_blacklist | array | no |
Public miner_id values to exclude from routing
|
A question’s kind selects how it is answered:
| Kind | Description |
|---|---|
| Choice |
A distribution over the listed options; the highest-probability option
is label
|
| Score |
A distribution over ordered levels; the expected level is
score
|
| Noul |
A yes/no question; requires exactly two options, and the answer exposes
probability_true
|
{
"answers": [
{
"id": "move",
"kind": "Choice",
"distribution": [0.148, 0.399, 0.131, 0.322],
"label": 1,
"score": 1.626,
"probability_true": 0.0,
"confidence": 0.076
}
],
"latency_ms": 8138.77,
"seed": 7247637261937428621,
"usage": {
"prompt_tokens": 106,
"completion_tokens": 1,
"total_tokens": 107
}
}curl https://perceptron.cloud/api/v1/decide \
-H "Authorization: Bearer $PERCEPTRON_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "bartmoss/io-3.5-control",
"state": "You are in a 5x5 grid. Snake head at (2,2) heading east. Food at (3,2).",
"questions": [
{
"id": "move",
"kind": "Choice",
"instructions": "Select the next move.",
"options": [
{ "label": "up", "description": "Move north" },
{ "label": "down", "description": "Move south" },
{ "label": "left", "description": "Move west" },
{ "label": "right", "description": "Move east" }
]
}
]
}'List Decision Models
GET /api/v1/decide/models No Auth
Returns all decision models, with their per-token decision pricing.
{
"object": "list",
"data": [
{
"id": "bartmoss/io-3.5-control",
"name": "Io 3.5 Control Preview",
"object": "model",
"created": 1748000000,
"owned_by": "perceptron",
"context_length": 8192,
"input_modalities": ["text"],
"output_modalities": ["decision"],
"supports_tools": false,
"supports_thinking": false,
"pricing": { "decision": { "input_per_1m": "x", "output_per_1m": "x" } },
"is_free": false
}
]
}curl https://perceptron.cloud/api/v1/decide/modelsCredits
GET /api/credits/balance Client or Master Key
Returns the current credit balance as a dollar string.
{
"balance": "25",
"currency": "USD"
}| Field | Type | Description |
|---|---|---|
| balance | string |
Current balance. “25” = $25.00
|
| currency | string |
Always “USD”
|
curl https://perceptron.cloud/api/credits/balance \
-H "Authorization: Bearer YOUR_API_KEY"import requests
response = requests.get(
"https://perceptron.cloud/api/credits/balance",
headers={"Authorization": "Bearer YOUR_API_KEY"},
)
print(response.json())
# {"balance": 250000000, "currency": "USD"}POST /api/credits/purchase Client or Master Key
Submits an on-chain USDT transaction hash for verification and
credits your account. The transaction must be a USDT transfer to the
Perceptron receiving address (from
GET /api/credits/receiving-address) on a supported chain.
Minimum purchase: $5. Maximum transaction age: 24 hours. Duplicate
hashes are rejected.
| Field | Type | Required | Description |
|---|---|---|---|
| tx_hash | string | yes |
On-chain transaction hash (0x…)
|
| token | string | yes |
Token symbol. Should be “USDT”
|
| amount | string | yes |
Dollar amount as decimal string, e.g. “25.00”
|
| chain_id | uint64 | yes |
EIP-155 chain ID of the network used, e.g. 8453 for Base.
See Billing for all supported chain IDs
|
{
"status": "ok",
"amount_credited": "2.5",
"new_balance": "5"
}| Field | Type | Description |
|---|---|---|
| amount_credited | string | Added to your balance |
| new_balance | string | Balance after crediting |
curl https://perceptron.cloud/api/credits/purchase \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"tx_hash": "0xabc123...",
"token": "USDT",
"amount": "25.00",
"chain_id": 8453
}'import requests, json
response = requests.post(
"https://perceptron.cloud/api/credits/purchase",
headers={"Authorization": "Bearer YOUR_API_KEY"},
json={
"tx_hash": "0xabc123...",
"token": "USDT",
"amount": "25.00",
"chain_id": 8453,
},
)
print(json.dumps(response.json(), indent=2))GET /api/credits/history Client or Master Key
Returns the full credit transaction history for the authenticated wallet.
{
"transactions": [
{
"id": "uuid",
"user_address": "0xabc...",
"tx_type": "Purchase",
"amount": "25",
"balance_after": "50",
"description": "Purchased $25.00 credits with USDT (tx: 0xabc...)",
"timestamp": 1748000000,
"tx_hash": "0xabc123...",
"status": "Completed"
}
]
}| Field | Type | Description |
|---|---|---|
| id | string | Transaction UUID |
| tx_type | string |
Purchase, ChatInference,
SttInference, TtsInference,
PersonaInference, WebSearch,
NewsFeed, Domain, Storage,
VideoInference, ImageInference,
VisionInference, AnimationRender,
Box, SpecialAward, or CreditAward
|
| amount | string | Positive = credit added, negative = debit |
| balance_after | string | Balance after this transaction |
| timestamp | uint64 | Unix seconds |
| tx_hash | string? |
On-chain hash. Only present on Purchase transactions
|
| status | string |
Pending, Completed, or Failed
|
curl https://perceptron.cloud/api/credits/history \
-H "Authorization: Bearer YOUR_API_KEY"import requests
response = requests.get(
"https://perceptron.cloud/api/credits/history",
headers={"Authorization": "Bearer YOUR_API_KEY"},
)
for tx in response.json()["transactions"]:
print(tx["tx_type"], tx["amount"], tx["status"])GET /api/credits/receiving-address No Auth
Returns the USDT receiving address that you should send funds to when
purchasing credits. The EVM address is the same across all supported EVM
chains; pass ?chain=solana to get the separate Solana
receiving address.
{
"address": "0x..."
}curl https://perceptron.cloud/api/credits/receiving-addressimport requests
response = requests.get("https://perceptron.cloud/api/credits/receiving-address")
print(response.json()["address"])Points
GET /api/points/balance Client or Master Key
Returns the authenticated wallet’s reward points balance. Points are non-monetary integers earned through platform activity.
{
"points": 350
}curl https://perceptron.cloud/api/points/balance \
-H "Authorization: Bearer YOUR_API_KEY"import requests
response = requests.get(
"https://perceptron.cloud/api/points/balance",
headers={"Authorization": "Bearer YOUR_API_KEY"},
)
print(response.json()["points"])GET /api/points/history Client or Master Key
Returns the points transaction history for the authenticated wallet.
{
"transactions": [
{
"id": "uuid",
"user_address": "0xabc...",
"trigger_type": "welcome_bonus",
"points_awarded": 250,
"description": "Welcome bonus: first purchase of $25.00 credits",
"timestamp": 1748000000,
"metadata": {"purchase_amount": 2500000000}
}
]
}| Field | Type | Description |
|---|---|---|
| trigger_type | string |
What caused the award. Known values: welcome_bonus,
credit_purchase, chat_usage,
feed_active, domain_active
|
| points_awarded | int64 | Points granted in this transaction |
| metadata | object? | Optional trigger-specific context (e.g. purchase amount) |
curl https://perceptron.cloud/api/points/history \
-H "Authorization: Bearer YOUR_API_KEY"import requests
response = requests.get(
"https://perceptron.cloud/api/points/history",
headers={"Authorization": "Bearer YOUR_API_KEY"},
)
for tx in response.json()["transactions"]:
print(tx["trigger_type"], tx["points_awarded"])Domains
Reserve a subdomain under pct.site and expose a local
port to the internet via an encrypted WebSocket tunnel. Billed from your
credit balance.
POST /api/domains/reserve Client or Master Key
Reserves a subdomain under pct.site. Deducts the first
hour’s cost immediately. Subdomains are first come, first served.
| Field | Type | Required | Description |
|---|---|---|---|
| subdomain | string | yes | 2–32 characters. Lowercase alphanumeric and hyphens only. No leading or trailing hyphens. Cannot be a reserved word |
Reserved words (rejected): www, api,
mail, ftp, admin,
tunnel, ns1, ns2,
mx, smtp, imap, pop,
app, dev, staging,
test
{
"id": "d1e2f3a4-...",
"subdomain": "myapp",
"full_domain": "myapp.pct.site",
"hourly_cost": "x",
"prepaid_until": 1748003600
}| Field | Type | Description |
|---|---|---|
| id | string | Domain UUID. Use it in all subsequent domain calls |
| full_domain | string | Public URL at which your tunnel will be reachable |
| hourly_cost | string | Billed per hour |
| prepaid_until | uint64 | Unix timestamp of the next billing event |
curl https://perceptron.cloud/api/domains/reserve \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"subdomain": "myapp"}'import requests, json
response = requests.post(
"https://perceptron.cloud/api/domains/reserve",
headers={"Authorization": "Bearer YOUR_API_KEY"},
json={"subdomain": "myapp"},
)
domain = response.json()
print(domain["full_domain"], domain["id"])GET /api/domains Client or Master Key
Lists all domains reserved by the authenticated wallet.
{
"domains": [
{
"id": "d1e2f3a4-...",
"subdomain": "myapp",
"full_domain": "myapp.pct.site",
"service_id": "domain-a1b2c3d4",
"created_at": 1748000000,
"local_port": 10001,
"tunnel_active": false
}
],
"base_domain": "pct.site",
"hourly_cost": "x",
"port_range": [10000, 10099]
}| Field | Type | Description |
|---|---|---|
| local_port | uint16? |
Assigned local port (10000–10099). null if not yet assigned
|
| tunnel_active | bool | Whether the WebSocket tunnel is currently connected |
| port_range | array |
Valid port range: [10000, 10099]
|
curl https://perceptron.cloud/api/domains \
-H "Authorization: Bearer YOUR_API_KEY"import requests
response = requests.get(
"https://perceptron.cloud/api/domains",
headers={"Authorization": "Bearer YOUR_API_KEY"},
)
for domain in response.json()["domains"]:
print(domain["full_domain"], "active:", domain["tunnel_active"])GET /api/domains/{id} Client or Master Key
Returns a single domain. You must own the domain.
| Parameter | Type | Description |
|---|---|---|
| id | string |
Domain UUID from POST /api/domains/reserve or GET
/api/domains
|
Same object shape as items in GET /api/domains.
curl https://perceptron.cloud/api/domains/d1e2f3a4-... \
-H "Authorization: Bearer YOUR_API_KEY"import requests
domain_id = "d1e2f3a4-..."
response = requests.get(
f"https://perceptron.cloud/api/domains/{domain_id}",
headers={"Authorization": "Bearer YOUR_API_KEY"},
)
print(response.json())POST /api/domains/{id}/assign Client or Master Key
Assigns a local port to the domain. The tunnel will route public
HTTPS traffic to localhost:{port}. The port must be in the
range 10000–10099 and not already assigned to another
domain of yours.
| Parameter | Type | Description |
|---|---|---|
| id | string | Domain UUID |
| Field | Type | Required | Description |
|---|---|---|---|
| port | uint16 | yes |
Local port that traffic will be routed to. Must be
10000–10099 inclusive
|
{
"status": "assigned",
"port": 10001
}curl https://perceptron.cloud/api/domains/d1e2f3a4-.../assign \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"port": 10001}'import requests
domain_id = "d1e2f3a4-..."
response = requests.post(
f"https://perceptron.cloud/api/domains/{domain_id}/assign",
headers={"Authorization": "Bearer YOUR_API_KEY"},
json={"port": 10001},
)
print(response.json())POST /api/domains/{id}/unassign Client or Master Key
Clears the port assignment from the domain. No request body required.
| Parameter | Type | Description |
|---|---|---|
| id | string | Domain UUID |
{"status": "unassigned"}curl -X POST https://perceptron.cloud/api/domains/d1e2f3a4-.../unassign \
-H "Authorization: Bearer YOUR_API_KEY"import requests
domain_id = "d1e2f3a4-..."
response = requests.post(
f"https://perceptron.cloud/api/domains/{domain_id}/unassign",
headers={"Authorization": "Bearer YOUR_API_KEY"},
)
print(response.json())POST /api/domains/{id}/tunnel-token Client or Master Key
Issues a short-lived JWT (5-minute TTL) for establishing a tunnel WebSocket connection to the domains server. A port must be assigned before calling this endpoint. The Perceptron frontend uses this automatically. You only need it if you are building a custom tunnel client.
| Parameter | Type | Description |
|---|---|---|
| id | string | Domain UUID |
{
"tunnel_token": "eyJ...",
"subdomain": "myapp",
"expires_at": 1748000300,
"local_port": 10001
}| Field | Type | Description |
|---|---|---|
| tunnel_token | string | JWT bearer token. Present it to the domains WebSocket server at connection time |
| expires_at | uint64 | Unix timestamp. The token is valid for 5 minutes from issuance |
| local_port | uint16 | Local port that traffic is routed to |
curl -X POST https://perceptron.cloud/api/domains/d1e2f3a4-.../tunnel-token \
-H "Authorization: Bearer YOUR_API_KEY"import requests
domain_id = "d1e2f3a4-..."
response = requests.post(
f"https://perceptron.cloud/api/domains/{domain_id}/tunnel-token",
headers={"Authorization": "Bearer YOUR_API_KEY"},
)
print(response.json()["tunnel_token"])POST /api/domains/add-external Client or Master Key
Adds a custom domain you own at an external registrar (free: no billing). Returns the DNS TXT challenge to publish and the A-record target. The domain serves traffic only after verification.
| Field | Type | Required | Description |
|---|---|---|---|
| domain | string | yes |
The apex domain, e.g. example.com. A leading
www. is stripped; the apex and its www alias
are both served
|
{
"id": "d1e2f3a4-...",
"kind": "external",
"full_domain": "example.com",
"hosts": ["example.com", "www.example.com"],
"txt_host": "_perceptron-challenge.example.com",
"txt_value": "8f3a...",
"external_ip": "167.172.147.149",
"verified": false
}POST /api/domains/verify Client or Master Key
Verifies ownership of a custom domain by reading back the DNS TXT
challenge. Publish TXT _perceptron-challenge.<domain> =
<txt_value> at your registrar first, then call this.
| Field | Type | Required | Description |
|---|---|---|---|
| domain_id | string | yes |
Domain UUID returned by add-external
|
POST /api/domains/check-dns Client or Master Key
Checks whether the domain’s A records point at the platform yet.
Point @ and (optionally) www at the
external_ip shown by add-external. Once at
least one resolves here, HTTPS is provisioned automatically.
| Field | Type | Required | Description |
|---|---|---|---|
| domain_id | string | yes |
Domain UUID returned by add-external
|
POST /api/domains/release Client or Master Key
Releases a reserved domain. The subdomain becomes available for others to reserve immediately. No refund for remaining prepaid time. You must own the domain.
| Field | Type | Required | Description |
|---|---|---|---|
| domain_id | string | yes | UUID of the domain to release |
{"status": "released"}curl https://perceptron.cloud/api/domains/release \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"domain_id": "d1e2f3a4-..."}'import requests
response = requests.post(
"https://perceptron.cloud/api/domains/release",
headers={"Authorization": "Bearer YOUR_API_KEY"},
json={"domain_id": "d1e2f3a4-..."},
)
print(response.json())Frontend-only domain endpoints: the following three
routes are only available on the local frontend server
(http://localhost:9300) and are used by the TUI. They are
not accessible on perceptron.cloud.
POST /api/domains/{id}/connect · POST /api/domains/{id}/disconnect · GET /api/domains/tunnels/status
News Feeds
Subscribe to real-time news streams. An active subscription is required to read items.
POST /api/feeds/news/subscribe Client or Master Key
Subscribes to the news feed. Deducts the first hour’s cost immediately. No request body required.
{
"status": "subscribed",
"prepaid_until": 1748003600,
"hourly_cost": "x"
}curl -X POST https://perceptron.cloud/api/feeds/news/subscribe \
-H "Authorization: Bearer YOUR_API_KEY"import requests
response = requests.post(
"https://perceptron.cloud/api/feeds/news/subscribe",
headers={"Authorization": "Bearer YOUR_API_KEY"},
)
print(response.json())POST /api/feeds/news/unsubscribe Client or Master Key
Cancels the active news feed subscription. No refund for remaining prepaid time. No request body required.
{"status": "unsubscribed"}curl -X POST https://perceptron.cloud/api/feeds/news/unsubscribe \
-H "Authorization: Bearer YOUR_API_KEY"import requests
response = requests.post(
"https://perceptron.cloud/api/feeds/news/unsubscribe",
headers={"Authorization": "Bearer YOUR_API_KEY"},
)
print(response.json())GET /api/feeds/news Client or Master Key
Returns a paginated list of news items. An active subscription is required.
| Parameter | Type | Default | Description |
|---|---|---|---|
| limit | uint | 50 |
Maximum items to return. Capped at 100
|
| category | string | - |
Filter by category ID. See GET /api/feeds/news/categories
for valid values
|
| before | uint64 | - | Unix timestamp cursor. Returns only items older than this value. Use for pagination |
{
"items": [
{
"id": "tweet-1234567890",
"timestamp": 1748000000,
"category": "crypto",
"headline": "Bitcoin surges past $100,000 as institutions buy",
"summary": "Bitcoin reached a new all-time high driven by institutional demand and ETF inflows.",
"source": "Reuters",
"link": "https://reuters.com/..."
}
]
}| Field | Type | Description |
|---|---|---|
| id | string |
Item ID in the format tweet-{tweet_id}
|
| timestamp | uint64 | Unix seconds of the original source post |
| category | string |
One of the category IDs from GET /api/feeds/news/categories
|
| headline | string | LLM-generated headline, max 15 words |
| summary | string | LLM-generated summary, max 40 words |
| source | string | Human-readable source name |
| link | string? | Original article URL, if available |
curl "https://perceptron.cloud/api/feeds/news?category=crypto&limit=20" \
-H "Authorization: Bearer YOUR_API_KEY"import requests
response = requests.get(
"https://perceptron.cloud/api/feeds/news",
headers={"Authorization": "Bearer YOUR_API_KEY"},
params={"category": "crypto", "limit": 20},
)
for item in response.json()["items"]:
print(item["timestamp"], item["headline"])GET /api/feeds/news/{id} Client or Master Key
Returns a single news item by ID. An active subscription is required.
| Parameter | Type | Description |
|---|---|---|
| id | string |
Item ID, e.g. tweet-1234567890
|
Single NewsItem object. Same shape as items in
GET /api/feeds/news.
curl https://perceptron.cloud/api/feeds/news/tweet-1234567890 \
-H "Authorization: Bearer YOUR_API_KEY"import requests
response = requests.get(
"https://perceptron.cloud/api/feeds/news/tweet-1234567890",
headers={"Authorization": "Bearer YOUR_API_KEY"},
)
print(response.json())GET /api/feeds/news/categories No Auth
Returns the list of available news categories. Use the
id values to filter GET /api/feeds/news.
{
"categories": [
{"id": "crypto", "name": "Crypto", "description": "Cryptocurrency market news"},
{"id": "politics", "name": "Politics", "description": "Political developments"},
{"id": "economics", "name": "Economics", "description": "Economic indicators and analysis"},
{"id": "technology", "name": "Technology", "description": "Technology and software news"},
{"id": "regulatory", "name": "Regulatory", "description": "Financial regulation updates"},
{"id": "entertainment", "name": "Entertainment", "description": "Media and cultural trends"},
{"id": "sports", "name": "Sports", "description": "Sports news and scores"}
]
}curl https://perceptron.cloud/api/feeds/news/categoriesimport requests
response = requests.get("https://perceptron.cloud/api/feeds/news/categories")
for cat in response.json()["categories"]:
print(cat["id"], "-", cat["description"])GET /api/feeds/news/status Client or Master Key
Returns the current subscription status for the news feed.
{
"feed_type": "news",
"status": "Active",
"subscribed_at": 1748000000,
"prepaid_until": 1748003600,
"hourly_cost": "x"
}| Field | Type | Description |
|---|---|---|
| status | string |
“Active” or “Inactive”
|
| subscribed_at | uint64? |
Unix seconds when subscription started. null if inactive
|
| prepaid_until | uint64? |
Unix seconds of the next billing event. null if inactive
|
| hourly_cost | string | Billed per hour |
curl https://perceptron.cloud/api/feeds/news/status \
-H "Authorization: Bearer YOUR_API_KEY"import requests
response = requests.get(
"https://perceptron.cloud/api/feeds/news/status",
headers={"Authorization": "Bearer YOUR_API_KEY"},
)
print(response.json())Web Search
Query the live web for current information. Billed per request.
POST /api/web_search Client or Master Key
Searches the web and returns structured results. Credits are reserved before the search executes; on failure the reservation is refunded.
| Field | Type | Required | Description |
|---|---|---|---|
| query | string | yes | The search query |
| count | integer | no | Number of results (1-20, default 8) |
{
"query": "latest ethereum gas prices",
"results": [
{
"title": "Ethereum Gas Tracker",
"url": "https://example.com/gas",
"snippet": "Current gas price: 12 gwei..."
}
]
}| Field | Type | Description |
|---|---|---|
| query | string | The original query string |
| results | array | Search result objects |
| results[].title | string | Result title |
| results[].url | string | Result URL |
| results[].snippet | string | Result snippet/description |
curl https://perceptron.cloud/api/web_search \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"query": "latest ethereum gas prices", "count": 5}'import requests, json
response = requests.post(
"https://perceptron.cloud/api/web_search",
headers={"Authorization": "Bearer YOUR_API_KEY"},
json={"query": "latest ethereum gas prices", "count": 5},
)
for result in response.json()["results"]:
print(result["title"], result["url"])API Keys
Client API keys (pct-...) are how you authenticate
agents and scripts without exposing your master key. All key management
endpoints require master key auth.
POST /api/keys/generate Master Key Only
Generates a new client API key. The full key is returned only once. It is never stored in plaintext and cannot be retrieved again. Accounts without a credit purchase are limited to 2 keys. Accounts with at least one completed credit purchase may hold up to 50 keys.
| Field | Type | Required | Description |
|---|---|---|---|
| label | string | yes | Human-readable label for identifying this key. 1–64 characters |
{
"id": "uuid",
"key": "pct-a1b2c3d4e5f6...",
"key_prefix": "pct-a1b2",
"label": "OpenClaw agent",
"created_at": 1748000000
}| Field | Type | Description |
|---|---|---|
| key | string | Full secret key. Copy it now. This is the only time it is shown |
| key_prefix | string |
First 8 characters for identification (e.g. pct-a1b2)
|
curl https://perceptron.cloud/api/keys/generate \
-H "Authorization: Bearer YOUR_MASTER_KEY" \
-H "Content-Type: application/json" \
-d '{"label": "OpenClaw agent"}'import requests, json
response = requests.post(
"https://perceptron.cloud/api/keys/generate",
headers={"Authorization": "Bearer YOUR_MASTER_KEY"},
json={"label": "OpenClaw agent"},
)
data = response.json()
print("Key (save this):", data["key"])
print("Prefix:", data["key_prefix"])GET /api/keys Master Key Only
Lists all client API keys for the authenticated wallet. Full key values are never returned, only prefixes and metadata.
{
"keys": [
{
"id": "uuid",
"key_prefix": "pct-a1b2",
"label": "OpenClaw agent",
"created_at": 1748000000,
"last_used_at": 1748001000
}
]
}curl https://perceptron.cloud/api/keys \
-H "Authorization: Bearer YOUR_MASTER_KEY"import requests
response = requests.get(
"https://perceptron.cloud/api/keys",
headers={"Authorization": "Bearer YOUR_MASTER_KEY"},
)
for key in response.json()["keys"]:
print(key["key_prefix"], key["label"], key["last_used_at"])DELETE /api/keys/{id} Master Key Only
Revokes a client API key. The key becomes invalid immediately. You must own the key.
| Parameter | Type | Description |
|---|---|---|
| id | string |
Key UUID from GET /api/keys
|
{"status": "revoked"}curl -X DELETE https://perceptron.cloud/api/keys/uuid-of-key \
-H "Authorization: Bearer YOUR_MASTER_KEY"import requests
key_id = "uuid-of-key"
response = requests.delete(
f"https://perceptron.cloud/api/keys/{key_id}",
headers={"Authorization": "Bearer YOUR_MASTER_KEY"},
)
print(response.json())Metrics
Usage and cost metrics for your account. All metrics endpoints
require master key auth. Responses support conditional requests via
ETag / If-None-Match (the server returns
304 Not Modified when unchanged).
GET /api/metrics/summary Master Key Only
Returns a snapshot of current account usage: balance, burn rate, runway, 24-hour totals, and per-service cost breakdown.
{
"credits_balance": "50",
"burn_rate_per_hour": "0.15",
"runway_hours": 333.33,
"total_tokens_24h": 50000,
"total_requests_24h": 12,
"total_cost_24h": "1.2",
"active_services": {
"domains": 1,
"feeds": 1,
"tunnels": 0
},
"cost_by_service": [
{"service": "chat", "cost": "0.9", "tokens": 50000},
{"service": "stt", "cost": "0.1", "tokens": 0},
{"service": "feed", "cost": "0.014", "tokens": 0},
{"service": "domain", "cost": "0.014", "tokens": 0},
{"service": "web_search", "cost": "0.05", "tokens": 0}
]
}| Field | Type | Description |
|---|---|---|
| credits_balance | string | Current balance |
| burn_rate_per_hour | string | Estimated hourly spend across all active services |
| runway_hours | float |
Hours until balance reaches zero at current burn rate. 0.0
if no active spend
|
| total_tokens_24h | int64 | Chat input + output tokens consumed in the last 24 hours |
| total_requests_24h | uint64 | Chat + STT requests in the last 24 hours |
| total_cost_24h | string | Total spend in the last 24 hours |
| cost_by_service[].service | string |
“chat”, “stt”, “tts”,
“feed”, “domain”, “storage”,
“web_search”, “video”, “image”,
“vision”, “animation”, or “box”
|
| cost_by_service[].cost | string | 24-hour spend for this service |
| cost_by_service[].tokens | int64 |
Non-zero only for “chat”
|
curl https://perceptron.cloud/api/metrics/summary \
-H "Authorization: Bearer YOUR_MASTER_KEY"import requests, json
response = requests.get(
"https://perceptron.cloud/api/metrics/summary",
headers={"Authorization": "Bearer YOUR_MASTER_KEY"},
)
data = response.json()
print(f"Balance: ${data['credits_balance'] / 100_000_000:.8f}")
print(f"Runway: {data['runway_hours']:.1f} hours")GET /api/metrics/history Master Key Only
Returns time-bucketed usage history. Use period and
bucket_minutes together to control the window and
granularity. Common combinations:
period=1&bucket_minutes=5 (last hour, 5-min buckets)
and period=24&bucket_minutes=120 (last 24 hours, 2-hour
buckets).
| Parameter | Type | Default | Range | Description |
|---|---|---|---|---|
| period | uint64 | 24 | 1–720 | Time window in hours |
| bucket_minutes | uint64 | 60 | 1–1440 | Width of each time bucket in minutes |
{
"period_hours": 24,
"buckets": [
{
"timestamp": 1747913600,
"chat_tokens": 5000,
"chat_cost": 12000000,
"stt_cost": 3000000,
"feed_cost": 2000000,
"domain_cost": 2000000,
"requests": 3
}
]
}| Field | Type | Description |
|---|---|---|
| timestamp | uint64 | Bucket start time as Unix seconds, aligned to the bucket boundary |
| chat_tokens | int64 | Input + output tokens from chat requests in this bucket |
| requests | uint64 | Chat + STT request count in this bucket |
curl "https://perceptron.cloud/api/metrics/history?period=24&bucket_minutes=120" \
-H "Authorization: Bearer YOUR_MASTER_KEY"import requests, json
response = requests.get(
"https://perceptron.cloud/api/metrics/history",
headers={"Authorization": "Bearer YOUR_MASTER_KEY"},
params={"period": 24, "bucket_minutes": 120},
)
data = response.json()
for bucket in data["buckets"]:
print(bucket["timestamp"], "tokens:", bucket["chat_tokens"])GET /api/metrics/keys Master Key Only
Returns usage metadata for all client API keys. Last used timestamps and labels are useful for auditing which keys are active.
{
"keys": [
{
"key_prefix": "pct-a1b2",
"label": "OpenClaw agent",
"created_at": 1748000000,
"last_used_at": 1748001000
}
]
}curl https://perceptron.cloud/api/metrics/keys \
-H "Authorization: Bearer YOUR_MASTER_KEY"import requests
response = requests.get(
"https://perceptron.cloud/api/metrics/keys",
headers={"Authorization": "Bearer YOUR_MASTER_KEY"},
)
for key in response.json()["keys"]:
print(key["key_prefix"], "last used:", key["last_used_at"])Wallet
GET /api/wallet/addresses Client or Master Key
Returns the EVM wallet address associated with the authenticated token.
{
"address": "0xabcdef1234567890..."
}curl https://perceptron.cloud/api/wallet/addresses \
-H "Authorization: Bearer YOUR_API_KEY"import requests
response = requests.get(
"https://perceptron.cloud/api/wallet/addresses",
headers={"Authorization": "Bearer YOUR_API_KEY"},
)
print(response.json()["address"])Personas
Create and manage Personas through the Perceptron app or the
Perceptron Telegram bot. You can list the personas you own via the API
(GET /api/personas, below).
GET /api/personas Client Key
List all personas owned by the authenticated user.
curl https://perceptron.cloud/api/personas \
-H "Authorization: Bearer YOUR_API_KEY"