Docs / Quickstart
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.
Get an account and a plan
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.
- Register an account with an email address and a username.
- Pick a tier on the pricing page. Standard variants cover the dashboard, API variants unlock external keys.
Protect a file from the dashboard
The dashboard is the fastest path and needs no key handling, because it signs requests with the internal key already attached to your account.
- 1.Open the dashboard and select a single
.pyfile of up to 10 MB. - 2.Adjust the settings panel, or leave the presets. The defaults are optimization level 4, Python 3.14, anti-debug off, lite function obfuscation on, and variable renaming on.
- 3.Submit. The job queues as
PENDING, moves toPROCESSING, and the download appears once it readsCOMPLETED.
Create an API key
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.
Submit a job to the API
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":"4","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."
}Poll the job and download
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>"Full Python client
Submit, poll, and download in one script. It raises on a failed job instead of silently writing an empty file.
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": "4",
"python_version": "3.14",
"anti_debug": "None",
"debug": False,
"wif": False,
"lite_fobf": True,
"no_console": False,
"func_obf": False,
"var_renaming": True,
"kod": False,
"pyinstaller": False,
"pytoc": 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)Settings reference
Every field below is optional. Omitted fields fall back to the presets shown, which are the same values the dashboard opens with.
| Key | Accepted values | Preset |
|---|---|---|
| optimization | "0" to "5"Level 0 applies nothing, 4 is the recommended balance, 5 is aggressive. | "4" |
| python_version | "3.10" to "3.14"Sets the bytecode target. Match the interpreter that will run the output. | "3.14" |
| anti_debug | "None", "Medium", "High", "Extreme"Runtime debugger, VM, and timing detection depth. | "None" |
| hwid | disk serial stringLocks the build to one machine. Leave empty for an unlocked build. | "" |
| trial_time | "1h", "1d", "1w", "1mo"Expires the build after the given window. Leave empty for no expiry. | "" |
| Boolean flag | Effect | Preset |
|---|---|---|
| lite_fobf | Compresses and marshals functions into compact byte arrays. | true |
| var_renaming | Renames variables, functions, and classes. | true |
| func_obf | Encrypts individual function bodies, decrypted at call time. | false |
| kod | Kill on detection. Terminates the process when tampering is seen. | false |
| wif | Wrap in function. Moves the whole module into one call scope. | false |
| no_console | Hides the console window on Windows builds. | false |
| pyinstaller | Generates a PyInstaller spec and compiles to an executable. | false |
| pytoc | Compiles the protected Python to a native extension through Cython. | false |
| debug | Emits debug prints from the pipeline. Keep this off for releases. | false |
Limits
Accepted input
A single .py file per request
Maximum upload size
10 MB
Submit rate limit
30 requests per minute per IP
Concurrent jobs
3 PENDING or PROCESSING jobs per account
Dashboard quota
500 obfuscations per month on the internal key
API key quota
1000 requests per day per external key
API keys per account
5 active keys
Download URL lifetime
1 hour from the moment it is issued
Errors you may hit
| Status | Message | Cause |
|---|---|---|
| 400 | File required | The multipart body had no file field. |
| 400 | Only .py files are supported | The filename did not end in .py. |
| 400 | File too large (max 10MB) | The upload exceeded 10 MB. |
| 401 | Invalid or revoked API key | The X-API-Key header did not match a live key. |
| 401 | API key required or session expired | No API key was sent and no valid session cookie was present. |
| 402 | Insufficient tokens. Please top up your account. | No active subscription and no tokens left. |
| 403 | API subscription required to use external API keys | The account is on a dashboard plan, not an API plan. |
| 403 | API subscription expired | The API subscription term has ended. |
| 429 | Too many requests | The per-IP submit rate limit was hit. |
| 429 | Too many concurrent jobs (max 3) | Three jobs are already PENDING or PROCESSING. |
| 429 | Daily limit reached (1000/day) | The external key exhausted its daily quota. |
| 404 | Job not found | The job id does not exist, or it belongs to another account. |
Where to go next
- How it works walks the five pipeline phases stage by stage.
- Features lists every protection module in the pipeline.
- Full documentation covers advanced flags and CI usage.
- Stuck on a build? Ask on Discord.
Quickstart FAQ
How large can a file be when I upload it to Nyami?
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.
Do I need an API subscription to call the Nyami API?
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.
How long is the Nyami download URL valid?
The signed downloadUrl returned on a COMPLETED job expires one hour after it is issued. Poll the job again to mint a fresh URL.
Which Python versions can Nyami target?
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.