Skip to main content

intigriti.py

CI Python License: MIT

An async, fully typed Python wrapper for the Intigriti researcher API — the API behind a researcher's personal access token, serving programs, their scope and rules of engagement, the program activity feed and payouts.

Which API? This wraps the researcher API only, at https://api.intigriti.com/external/researcher. Intigriti also runs a separate company API for organisations automating their own programs; it has its own host, authentication and payloads, and is not covered here.

Status: early development — the public API of this library may still change.

What's covered

Every endpoint of researcher API v1:

Endpoint Method
GET /v1/programs client.programs.list() · .iterate()
GET /v1/programs/{programId} client.programs.get()
GET /v1/programs/activities client.programs.activities() · .iterate_activities()
GET /v1/programs/{programId}/domains/{versionId} client.programs.domains()
GET /v1/programs/{programId}/rules-of-engagements/{versionId} client.programs.rules_of_engagement()
GET /v1/payouts (BETA) client.payouts.list() · .iterate()

Requirements

  • Python 3.13+

Installation

Not published on PyPI yet — install from source:

uv add git+https://github.com/DonAsako/intigriti.py
# or
pip install git+https://github.com/DonAsako/intigriti.py

Usage

Generate a personal access token from your Intigriti account settings, then:

import asyncio
import os

import intigriti


async def main() -> None:
    async with intigriti.Client(os.environ["INTIGRITI_TOKEN"]) as client:
        page = await client.programs.list(status=intigriti.ProgramStatus.OPEN, limit=100)
        print(f"{page.max_count} programs available")

        # Pages are walked for you; the API serves 50 records at a time by default.
        async for program in client.programs.iterate(following=True):
            print(program.handle, program.max_bounty.value, program.max_bounty.currency)

        detail = await client.programs.get(page.records[0].id)
        for domain in detail.domains.content or []:
            print(domain.endpoint, domain.tier.value)


asyncio.run(main())

The same example, ready to run, lives in examples/quickstart.py:

INTIGRITI_TOKEN=xxxxx uv run python examples/quickstart.py

Everything is typed: responses are Pydantic models with snake_case attributes, epoch timestamps decoded to aware datetimes and bounties to Decimal.

Enumerations

The API returns enumerations as {"id": 3, "value": "Open"}. Only the id is contractual, so compare against the members of intigriti.enums rather than against the label:

if program.status.id == intigriti.ProgramStatus.OPEN:
    ...

An id this library does not know is decoded as a plain integer rather than raising, so a new program status will not break a running client.

Errors

Failed requests raise an IntigritiAPIError subclass carrying the status, the API error code and the identifier to quote when reporting a problem:

try:
    await client.programs.get(program_id)
except intigriti.IntigritiRateLimitError as exc:
    print("throttled, retry after", exc.retry_after)
except intigriti.IntigritiAPIError as exc:
    print(exc.status_code, exc.code, exc.identifier)

Note that Intigriti signals rate limiting with HTTP 403 rather than 429, so IntigritiRateLimitError inherits from IntigritiPermissionError.

Development

Requires uv.

uv sync                    # install deps + dev tools
uv run pre-commit install  # install git hooks (pre-commit + commit-msg)

With just, just install does both.

uv run ruff check .      # lint
uv run ruff format .     # format
uv run mypy              # type-check
uv run pytest            # tests
uv run pytest --cov      # tests with coverage
just            # list all recipes
just fix        # auto-fix lint + format
just check      # lint + format-check + typecheck + test (mirrors CI)

Tooling

Tool Purpose
uv Dependency & virtualenv management
Hatchling PEP 517 build backend
Ruff Linter + formatter
Mypy Static type checking (strict mode)
Pytest Test runner (+ coverage, asyncio, benchmark, xdist, mock, timeout, dotenv)
pre-commit Git hooks orchestration
gitlint Conventional Commits enforcement
GitHub Actions CI: lint, type-check, tests on every push/PR
just Task runner for common dev commands (optional)

Layout

.
├── intigriti/              # source package
│   ├── client.py           # public entry point
│   ├── enums.py            # documented enumeration ids
│   ├── exceptions.py       # error hierarchy
│   ├── models/             # typed response models
│   ├── resources/          # endpoint groups (programs, payouts)
│   ├── pagination.py       # limit/offset iteration
│   ├── _http.py            # async transport
│   └── py.typed            # PEP 561 marker (ships type hints to consumers)
├── examples/
│   └── quickstart.py
├── tests/
│   └── unit/
├── .github/
│   ├── workflows/ci.yml    # CI pipeline
│   ├── ISSUE_TEMPLATE/
│   ├── PULL_REQUEST_TEMPLATE.md
│   ├── CONTRIBUTING.md
│   ├── SECURITY.md
│   └── dependabot.yml
├── .pre-commit-config.yaml
├── .editorconfig
├── .gitlint
├── .yamllint.yaml
├── justfile
├── pyproject.toml          # single source of truth (ruff, mypy, pytest, coverage)
└── uv.lock

Contributing

See CONTRIBUTING.md. Commits follow Conventional Commits and are validated by gitlint.

Disclaimer

Unofficial project, not affiliated with or endorsed by Intigriti.

License

MIT

Release files for intigriti 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for intigriti 0.1.0
File Size Uploaded
intigriti-0.1.0.tar.gz 21.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for intigriti 0.1.0
File Interpreter ABI Platform
intigriti-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 45.0 kB

Release files / intigriti-0.1.0.tar.gz

Download URL intigriti-0.1.0.tar.gz
Size 21.1 kB
Tags Source
SHA-256 checksum
How to use checksums
a5acef4ca99de8b445c1736c34e367d350f233a936b48d06f5bee8583f5e82e9
BLAKE2b-256 checksum
How to use checksums
75419dae282ee90b9fd2273a406d27392508928e791889ca32f2521736ce0572
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 18, 2026.

Transparency log

Release files / intigriti-0.1.0-py3-none-any.whl

Download URL intigriti-0.1.0-py3-none-any.whl
Size 23.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
bc1d2c412b159e7efa5e76f2e104f301a9fde1e50f9cf9c5d5a4242658bd1989
BLAKE2b-256 checksum
How to use checksums
61fae9248a4d7a02ee1d758dfded90e2b0ff44481068e8adf118e72ead2a9a0c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 18, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page