REST API v1

Bytecode recovery that fits your pipeline.

Upload authorized Python bytecode, poll an isolated job, and download audited Python source. The API uses the same private balance and retention policy as the dashboard.

Never executes uploads

Static recovery only

120 requests/min

Independent per key

Idempotent uploads

Safe automatic retries

Quick start

Create a job in one request.

Keep the key in an environment variable, send the original filename, and generate one idempotency key per logical upload.

curl -X POST "$PYDECODE_BASE_URL/decompile" \
  -H "Authorization: Bearer $PYDECODE_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "X-Filename: module.pyc" \
  -H "Content-Type: application/octet-stream" \
  --data-binary @module.pyc

Authentication

One revocable key per integration.

Create up to ten independently revocable keys. The full secret appears once and is stored by PyDecode only as a keyed hash.

Bearer authentication

Send Authorization: Bearer pyc_live_… with every request. Never place a key in a query string or client-side repository.

Idempotency

POST /decompile requires an 8–128 character key. Repeating it returns the original job without reserving credits twice.

Reference

Endpoints

All JSON responses include X-Request-Id, X-API-Version, and per-key rate-limit headers.

GET/200

Test connection

Validate the key and inspect the available v1 routes.

{
  "name": "PyDecode API",
  "version": "v1",
  "endpoints": { "credits": "/credits", "decompile": "/decompile" }
}
GET/credits?limit=20200

Check credits

Read the shared balance and recent immutable ledger entries.

{
  "credits_available": 42,
  "ledger": []
}
POST/decompile202

Create a recovery job

Send one supported Python PYC or ZIP as the raw body. A new job returns 202; an idempotent replay returns 200.

{
  "job": {
    "id": "cm...",
    "state": "queued",
    "status_url": "https://.../api/v1/jobs/cm..."
  },
  "credits_available": 41,
  "idempotent_replay": false
}
GET/jobs?limit=20&cursor=…200

List jobs

Page through private jobs, newest first, with an opaque cursor.

{
  "data": [{ "id": "cm...", "state": "succeeded" }],
  "next_cursor": null
}
GET/jobs/{id}200

Inspect a job

Poll state, credit reconciliation, audit counts, and event history.

{
  "job": {
    "id": "cm...",
    "state": "succeeded",
    "result_url": "https://.../api/v1/jobs/cm.../result"
  }
}
GET/jobs/{id}/result200 ZIP

Download result

Download the private ZIP after the job succeeds or partially succeeds.

Content-Type: application/zip
Content-Disposition: attachment; filename="recovered-source.zip"

Client kits

Import, run, and adapt.

Every downloadable example reads credentials from the environment. The collection includes response assertions and job-ID chaining.

Reliability

Errors you can automate around.

Errors use a stable JSON envelope. Log the request ID, honor Retry-After, and retry uploads with the original idempotency key.

400invalid_requestFix headers, filename, cursor, or body.
401invalid_api_keyReplace or rotate the credential.
402insufficient_creditsAdd credits before retrying.
409result_not_readyWait for Retry-After, then poll again.
413payload_too_largeKeep compressed uploads within 25 MB.
429rate_limit_exceededBack off until X-RateLimit-Reset.

Ready to verify it?

Run a signed request without leaving the dashboard.

The in-browser console holds your key in memory only.

Open console