Nyami
FeaturesPricing

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 a planFull documentation
01

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.
02

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. 1.Open the dashboard and select a single .py file of up to 10 MB.
  2. 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. 3.Submit. The job queues as PENDING, moves to PROCESSING, and the download appears once it reads COMPLETED.
03

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.

04

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.

Request
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}'
200 response
{
  "jobId": "clx0a1b2c3d4e5f6g7h8i9j0",
  "message": "Job submitted successfully. Poll GET /api/obfuscate/[jobId] for status."
}
05

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.

Request
curl https://nyami.cc/api/obfuscate/clx0a1b2c3d4e5f6g7h8i9j0 \
  -H "X-API-Key: nyami_your_key_here"
200 response
{
  "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=..."
}
Download
curl -L -o protected.py "<downloadUrl from the COMPLETED response>"
06

Full Python client

Submit, poll, and download in one script. It raises on a failed job instead of silently writing an empty file.

client.py
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)
07

Settings reference

Every field below is optional. Omitted fields fall back to the presets shown, which are the same values the dashboard opens with.

KeyAccepted valuesPreset
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"
hwiddisk 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 flagEffectPreset
lite_fobfCompresses and marshals functions into compact byte arrays.true
var_renamingRenames variables, functions, and classes.true
func_obfEncrypts individual function bodies, decrypted at call time.false
kodKill on detection. Terminates the process when tampering is seen.false
wifWrap in function. Moves the whole module into one call scope.false
no_consoleHides the console window on Windows builds.false
pyinstallerGenerates a PyInstaller spec and compiles to an executable.false
pytocCompiles the protected Python to a native extension through Cython.false
debugEmits debug prints from the pipeline. Keep this off for releases.false
08

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

09

Errors you may hit

StatusMessageCause
400File requiredThe multipart body had no file field.
400Only .py files are supportedThe filename did not end in .py.
400File too large (max 10MB)The upload exceeded 10 MB.
401Invalid or revoked API keyThe X-API-Key header did not match a live key.
401API key required or session expiredNo API key was sent and no valid session cookie was present.
402Insufficient tokens. Please top up your account.No active subscription and no tokens left.
403API subscription required to use external API keysThe account is on a dashboard plan, not an API plan.
403API subscription expiredThe API subscription term has ended.
429Too many requestsThe per-IP submit rate limit was hit.
429Too many concurrent jobs (max 3)Three jobs are already PENDING or PROCESSING.
429Daily limit reached (1000/day)The external key exhausted its daily quota.
404Job not foundThe job id does not exist, or it belongs to another account.
10

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.

NYAMI

Python protection through compilation, encryption, and active defense.

40+ protection modules

Product

  • Features
  • Pricing
  • Comparison
  • Purchase

Developers

  • Quickstart
  • Documentation
  • Blog

Support

  • Discord
  • projectnyami@proton.me

© 2026 Nyami. All rights reserved.

Terms of ServicePrivacy PolicyRefund Policy
NYAMI