API Reference

Submit Python files for protection and retrieve compiled artifacts programmatically. All API endpoints are under /api/v1/ and require an API key.

Overview

The PyVMProtect API lets you integrate Python protection into your own build pipeline. The typical flow is:

  1. Submit a .py or .zip file → receive a job_id
  2. Poll the job status until status is complete or error
  3. (Optional) Confirm Cython hot-paths if status reaches hot_path_pending
  4. Download the protected artifact
An account is required to use the API. Create a free account to get started.

Authentication

All /api/v1/ endpoints require an API key in the Authorization header:

Authorization: Bearer pvmp_your_api_key_here

To generate your API key, go to My Account and click Generate Key in the API Key section. Regenerating a key immediately invalidates the previous one.

Keep your API key secret. Do not commit it to version control or expose it in client-side code.

Endpoints

POST /api/v1/job_submit.php

Upload a Python file or project zip and start a build job.

Request

Content-Type: multipart/form-data

FieldTypeDescription
filefileRequired. .py or .zip (max 32 MB; zip max 256 MB uncompressed)
module_namestringOutput module name. Defaults to main. Alphanumeric and underscores only.
optionsJSON stringOptional build options object (see options below)
hot_pathsJSON arrayOptional list of function names to pre-select for Cython protection

Response 200

{
  "job_id":      "a3f9bc12e4561234a3f9bc12e4561234",
  "status":      "queued",
  "module_name": "my_module",
  "is_zip":      false
}
GET /api/v1/job_status.php?job_id=<id>

Poll the current status of a build job. Call this every 1–3 seconds until status is complete or error.

Response 200

{
  "job_id":         "a3f9bc12e4561234...",
  "status":         "building",
  "module_name":    "my_module",
  "is_zip":         false,
  "progress_pct":   45,
  "progress_label": "Compiling extensions",
  "created_at":     1713700000,
  "updated_at":     1713700042,

  // Present when status = "hot_path_pending":
  "functions":  ["encrypt_payload", "verify_license", "decode_config"],

  // Present when status = "complete":
  "download_token": "abc123..."
}
POST /api/v1/job_hotpath.php

Confirm which functions to Cython-compile when a job is in hot_path_pending state. Pass an empty array to skip Cython optimization and continue with standard protection.

Request body (JSON)

{
  "job_id":    "a3f9bc12e4561234...",
  "hot_paths": ["encrypt_payload", "verify_license"]
}

Response 200

{ "ok": true, "job_id": "...", "hot_paths": ["encrypt_payload", "verify_license"] }
GET /api/v1/job_download.php?job_id=<id>

Download the protected artifact as a .zip file. Only available when status = complete. The artifact is deleted after the first successful download (one-time link, 1-hour TTL).

Response

Binary application/zip stream with Content-Disposition: attachment. On error, returns JSON.

Full Workflow

The complete lifecycle of a build job:

POST /api/v1/job_submit.php          → { job_id }
  ↓
GET  /api/v1/job_status.php?job_id=  → status: queued
  ↓  (poll every 2s)
GET  /api/v1/job_status.php?job_id=  → status: building, progress_pct: 45
  ↓
GET  /api/v1/job_status.php?job_id=  → status: hot_path_pending, functions: [...]
  ↓  (optional — only if Cython is enabled)
POST /api/v1/job_hotpath.php         → { ok: true }
  ↓
GET  /api/v1/job_status.php?job_id=  → status: complete, download_token: "..."
  ↓
GET  /api/v1/job_download.php?job_id= → .zip download

Code Examples

curl

# 1. Submit a file
curl -X POST https://pyvmprotect.com/api/v1/job_submit.php \
  -H "Authorization: Bearer pvmp_your_key_here" \
  -F "file=@my_script.py" \
  -F "module_name=my_module"

# 2. Poll status
curl https://pyvmprotect.com/api/v1/job_status.php?job_id=JOB_ID \
  -H "Authorization: Bearer pvmp_your_key_here"

# 3. Download when complete
curl -O https://pyvmprotect.com/api/v1/job_download.php?job_id=JOB_ID \
  -H "Authorization: Bearer pvmp_your_key_here"

Python

import time
import requests

API_KEY = "pvmp_your_key_here"
BASE    = "https://pyvmprotect.com/api/v1"
HEADERS = {"Authorization": f"Bearer {API_KEY}"}

# 1. Submit
with open("my_script.py", "rb") as f:
    resp = requests.post(
        f"{BASE}/job_submit.php",
        headers=HEADERS,
        files={"file": ("my_script.py", f, "text/x-python")},
        data={"module_name": "my_module"},
    )
resp.raise_for_status()
job_id = resp.json()["job_id"]
print(f"Job submitted: {job_id}")

# 2. Poll until done
while True:
    status = requests.get(
        f"{BASE}/job_status.php",
        headers=HEADERS,
        params={"job_id": job_id},
    ).json()

    s = status["status"]
    print(f"  {s} ({status.get('progress_pct', 0)}%)")

    if s == "hot_path_pending":
        # Confirm all suggested functions (or pass a subset)
        requests.post(
            f"{BASE}/job_hotpath.php",
            headers=HEADERS,
            json={"job_id": job_id, "hot_paths": status["functions"]},
        ).raise_for_status()

    elif s == "complete":
        break

    elif s == "error":
        raise RuntimeError(f"Build failed: {status.get('error')}")

    time.sleep(2)

# 3. Download
resp = requests.get(
    f"{BASE}/job_download.php",
    headers=HEADERS,
    params={"job_id": job_id},
)
resp.raise_for_status()
with open("my_module_protected.zip", "wb") as f:
    f.write(resp.content)
print("Downloaded: my_module_protected.zip")

Status Values

StatusMeaning
queuedJob is waiting in the build queue
buildingDaemon is actively compiling the submission
hot_path_pendingAwaiting your hot-path selection — call job_hotpath.php to continue
completeBuild succeeded — artifact available for download
errorBuild failed — see the error field for details

Rate Limits

EndpointLimit
job_submit.php20 submissions per hour per account

When a rate limit is exceeded the API returns HTTP 429 with an error message. Retry after the window resets (hourly).

Errors

All errors return JSON with an error field:

{ "error": "Invalid API key" }
HTTP CodeMeaning
400Bad request — missing or invalid parameter
401Missing or invalid API key
404Job not found (or belongs to another account)
405Wrong HTTP method
409Job is in the wrong state for this operation
410Artifact already downloaded or link expired
413File too large
429Rate limit exceeded
500Server error — contact support