Skip to main content

Threat.Zone Python SDK

Official Python SDK for the Threat.Zone malware analysis platform. Targets the Threat.Zone Public API, ships fully typed Pydantic v2 models for every endpoint, and exposes both synchronous (ThreatZone) and asynchronous (AsyncThreatZone) clients with identical method surfaces. Requires Python 3.10+.

[!WARNING]

⚠️ Version Compatibility Notice

This SDK requires Threat.Zone v3.2.0 or later.

This SDK targets the Public API shipped with Threat.Zone v3.2.0. It will not function correctly against earlier versions of the platform.

Running an older Threat.Zone version? Pin to a pre-v3.2.0 release of this SDK, or coordinate with your admin to upgrade the platform first.

Install

pip install threatzone
# or
uv add threatzone

Configure

from threatzone import ThreatZone

client = ThreatZone(api_key="<your-api-key>")

The client reads THREATZONE_API_KEY from the environment, so ThreatZone() is enough if you export it. The default base_url is https://app.threat.zone/public-api.

For on-prem or local development, pass base_url explicitly:

client = ThreatZone(
    api_key="<your-api-key>",
    base_url="https://threatzone.your-company.internal/public-api",
)

The base_url must include the /public-api suffix. For self-signed certs, leave verify_ssl=False (the default).

Quick usage

from pathlib import Path

account = client.get_user_info()
print(f"Workspace: {account.workspace_name}")

submission = client.create_sandbox_submission(Path("./sample.exe"))
completed = client.wait_for_completion(submission.uuid, timeout=600)
print(f"Verdict: {completed.level}")
print(f"MITRE techniques: {completed.mitre_techniques}")
  • Pass auto_select_environment=True to let the server route the sample to a Windows / Linux / macOS / Android environment based on its detected type. See recipe 14.

Filename canonicalisation (dynamic_mimetype_check)

By default the server appends the real (Magika-detected) extension to the persisted filename when it disagrees with what the file was named. Opt out per submission — sandbox / open-in-browser take it as a metafields key, static / CDR take it as a top-level kwarg:

# sandbox / open-in-browser → inside metafields
client.create_sandbox_submission(
    "./malware.txt",
    environment="w10_x64",
    metafields={"dynamic_mimetype_check": False, "timeout": 120},
)

# static / CDR → top-level kwarg
client.create_static_submission("./malware.txt", dynamic_mimetype_check=False)
client.create_cdr_submission("./report.doc", dynamic_mimetype_check=False)

See Recipe 13 for the full breakdown including reading back FileInfo.is_mimetype_checked after the submission completes.

Core concepts

  • Submissions — one analysis target (file or URL) with a stable uuid, a workspace owner, a private flag, and up to four reports.
  • Reportsdynamic, static, cdr, url_analysis. Independent; poll each via submission.is_complete() / submission.has_errors(). Every report carries its own status, level, score.
  • Observation streams — the dynamic report produces indicators (rule hits), iocs (concrete IOC values), behaviours (OS-level operations), and syscalls (raw log lines).
  • Artifacts — files produced or extracted during analysis; keyed by hashes.sha256.
  • Access control — public submissions are visible to every API key; private submissions are workspace-scoped. Cross-workspace reads of private submissions raise PermissionDeniedError (403).

Async

AsyncThreatZone mirrors ThreatZone method-for-method:

import asyncio
from threatzone import AsyncThreatZone

async def main() -> None:
    async with AsyncThreatZone(api_key="<your-api-key>") as client:
        account = await client.get_user_info()
        print(account.email)

asyncio.run(main())

Use asyncio.gather() for concurrent fan-out. See recipe 10.

Errors

Every error inherits from ThreatZoneError. HTTP errors additionally inherit from APIError and carry the parsed error envelope on .body, with the structured code at .body["code"]. Branch on .code, never on .message.

Status Exception When
ThreatZoneError Base class. Catch for a single safety net.
APIError HTTP error base class. .status_code, .body, .response.
400 BadRequestError INVALID_UUID, INVALID_QUERY_PARAM.
401 AuthenticationError Missing/invalid API key.
402 PaymentRequiredError Workspace subscription inactive.
403 PermissionDeniedError Cross-workspace private read, or module not enabled.
404 NotFoundError Submission, artifact, or media missing.
409 ReportUnavailableError Required report not yet available. Carries .code, .submission_uuid, .required_report, .current_status, .available_reports.
429 RateLimitError API quota exceeded. Has .retry_after.
5xx InternalServerError Server-side failure.
TimeoutError HTTP request timed out.
ConnectionError Network-level failure.
AnalysisTimeoutError wait_for_completion() exceeded its timeout. .uuid, .elapsed.
202 YaraRulePendingError YARA rule generation still in progress. .retry_after.
from threatzone import ReportUnavailableError, ThreatZone

try:
    indicators = client.get_indicators("00000000-0000-0000-0000-000000000000")
except ReportUnavailableError as exc:
    if exc.code == "DYNAMIC_REPORT_UNAVAILABLE":
        print(f"Dynamic report not ready (status: {exc.current_status}).")

See recipe 9 for the full discrimination pattern and recipe 7 for the recommended retry loop.

Documentation

Document Purpose
Recipes Runnable examples covering common tasks.
Type Reference Pydantic model overview grouped by feature area.

Testing utilities

The SDK ships a stateful in-process fake Threat.Zone API for consumer-side tests:

from threatzone import ThreatZone
from threatzone.testing import FakeThreatZoneAPI, scenarios

fake = FakeThreatZoneAPI()
scenarios.seed_malicious_pe(fake, sha256="a" * 64)

client = ThreatZone(
    api_key="test-key",
    base_url="https://fake.threat.zone/public-api",
    http_client=fake.as_httpx_client(),
)

Available scenarios: seed_malicious_pe, seed_benign_document, seed_cdr_document, seed_phishing_url, seed_static_only_submission, seed_private_cross_workspace.

Development

uv sync --all-extras --dev
uv run pytest tests/
uv run ruff check src/ tests/
uv run mypy src/threatzone
uv build

All tests run in-process against FakeThreatZoneAPI — no network, no API token. Live smoke-testing uses the scripts in examples/ with PUBLIC_API_TOKEN exported; see examples/README.md.

GitHub Actions workflows can be exercised locally via act — see .github/workflows/README.md.

Links

License

MIT — see LICENSE.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

threatzone-1.1.1.tar.gz (161.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

threatzone-1.1.1-py3-none-any.whl (67.0 kB view details)

Uploaded Python 3

File details

Details for the file threatzone-1.1.1.tar.gz.

File metadata

  • Download URL: threatzone-1.1.1.tar.gz
  • Upload date:
  • Size: 161.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for threatzone-1.1.1.tar.gz
Algorithm Hash digest
SHA256 d1fc5f3b76cc7e623eacb3c1dd9766c844ab60be071ff6c6c4ec4680eb21ee19
MD5 db86e2e6e23b8e2fdbbf7fb611dd2365
BLAKE2b-256 6a17523f92be0d37a05c59a276d0f0578e61ed66965015839a5fdb38ee58c6b5

See more details on using hashes here.

Provenance

The following attestation bundles were made for threatzone-1.1.1.tar.gz:

Publisher: publish.yml on Malwation/threatzone-python-sdk

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file threatzone-1.1.1-py3-none-any.whl.

File metadata

  • Download URL: threatzone-1.1.1-py3-none-any.whl
  • Upload date:
  • Size: 67.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for threatzone-1.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 b0530ec4a04b758c8a07a0fa063a42b46143447405ea3f8ab08c5809f525cbd4
MD5 49e540742957dddde067bd134a6d288b
BLAKE2b-256 57f9fa25259eed3725238eaf20d64a26850172f6ce062eebd1e7f53af6e47f23

See more details on using hashes here.

Provenance

The following attestation bundles were made for threatzone-1.1.1-py3-none-any.whl:

Publisher: publish.yml on Malwation/threatzone-python-sdk

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page