Docs / Quickstart
Protect your first Python file in under five minutes. Upload it through the dashboard, or submit it to the API and poll until the protected build is ready to download.
Create an account, then activate a plan. Every plan ships the full protection pipeline with no feature gating. Dashboard uploads work on any active plan. The HTTP API additionally requires an API plan, because that is what allows external keys to be used.
The dashboard is the fastest path and needs no key handling, because it signs requests with the internal key already attached to your account.
.py file of up to 10 MB.PENDING, moves to PROCESSING, and the download appears once it reads COMPLETED.Keys are issued from dashboard API keys on an API plan. The full key is shown exactly once at creation, so store it in a secret manager or an environment variable before closing the dialog. Only the nyami_ prefix is retrievable afterwards. You can hold five active keys, and revoking one frees a slot.
Send it on every request as the X-API-Key header. Never put it in client-side code, a repository, or a query string.
POST /api/obfuscate takes a multipart body with exactly two fields: file, the .py source, and settings, a JSON string. It responds with the job id, not the protected file.
curl -X POST https://nyami.cc/api/obfuscate \
-H "X-API-Key: nyami_your_key_here" \
-F "file=@script.py" \
-F 'settings={"optimization":"1","python_version":"3.14","anti_debug":"none","lite_fobf":true,"var_renaming":true}'{
"jobId": "clx0a1b2c3d4e5f6g7h8i9j0",
"message": "Job submitted successfully. Poll GET /api/obfuscate/[jobId] for status."
}GET /api/obfuscate/{jobId} returns the current status. A COMPLETED job also carries outputFileSize and a signed downloadUrl that is valid for one hour. A FAILED job carries errorMessage instead. Poll on a five second interval.
curl https://nyami.cc/api/obfuscate/clx0a1b2c3d4e5f6g7h8i9j0 \
-H "X-API-Key: nyami_your_key_here"{
"jobId": "clx0a1b2c3d4e5f6g7h8i9j0",
"status": "COMPLETED",
"inputFileName": "script.py",
"inputFileSize": 4821,
"createdAt": "2026-08-15T10:04:11.204Z",
"startedAt": "2026-08-15T10:04:12.881Z",
"completedAt": "2026-08-15T10:04:39.552Z",
"outputFileSize": 261774,
"downloadUrl": "https://<your-app>/api/obfuscate/<jobId>/download?token=..."
}curl -L -o protected.py "<downloadUrl from the COMPLETED response>"Submit, poll, and download in one script. It raises on a failed job instead of silently writing an empty file. The settings object carries the defaults, and the settings page documents every remaining key, including the lowering families.
import json
import time
import requests
API_KEY = "nyami_your_key_here"
BASE = "https://nyami.cc/api/obfuscate"
HEADERS = {"X-API-Key": API_KEY}
settings = {
"optimization": "1",
"python_version": "3.14",
"anti_debug": "none",
"anti_debug_native": False,
"debug": False,
"wif": False,
"lite_fobf": True,
"func_obf": True,
"var_renaming": True,
"mixed_format": True,
"whitebox": True,
"zip_pyc": False,
"zip_light": True,
"windows_target": True,
"drv": False,
"no_console": False,
"pyinstaller": False,
"icon": "",
"kod": False,
"pytoc": False,
"rename_public_api": False,
"hwid": "",
"trial_time": "",
}
with open("script.py", "rb") as handle:
submit = requests.post(
BASE,
headers=HEADERS,
files={"file": ("script.py", handle, "text/x-python")},
data={"settings": json.dumps(settings)},
timeout=120,
)
submit.raise_for_status()
job_id = submit.json()["jobId"]
print(f"submitted {job_id}")
while True:
poll = requests.get(f"{BASE}/{job_id}", headers=HEADERS, timeout=30)
poll.raise_for_status()
job = poll.json()
status = job["status"]
if status == "COMPLETED":
download = requests.get(job["downloadUrl"], timeout=300)
download.raise_for_status()
with open("protected.py", "wb") as out:
out.write(download.content)
print(f"wrote protected.py ({len(download.content)} bytes)")
break
if status == "FAILED":
raise RuntimeError(f"job {job_id} failed: {job.get('errorMessage', 'no detail returned')}")
time.sleep(5)This walkthrough covers the shortest path through the product. Every setting the pipeline accepts, with the values it takes and what it changes, is on the settings page. The endpoint contract, the rate and concurrency limits, and every error code the API can return are on the API page.
Each submission accepts a single .py file up to 10 MB. Larger projects should be entry-point protected, or split before submission. Ask on Discord if you need a higher cap.
Yes. External API keys only work on an API subscription with an unexpired term. Dashboard uploads work on any active plan because they use your internal key.
The signed downloadUrl returned on a COMPLETED job expires one hour after it is issued. Poll the job again to mint a fresh URL.
You can target Python 3.10, 3.11, 3.12, 3.13, or 3.14 through the python_version setting. It defaults to 3.14.