Skip to main content

agvd — Python client for the African Genome Variation Database

agvd is a Python client for the African Genome Variation Database (AGVD) GraphQL API — a typed, paginated, retrying AGVDClient for programmatic use, plus the agvd command-line tool for bulk VCF/table annotation that ships on top of it.


Installation

# The library only — AGVDClient, models, exceptions. Needs just `requests`.
pip install agvd

# + the bulk file-processing CLI (pandas, pysam, openpyxl, tqdm)
pip install "agvd[cli]"

Requires Python 3.10+.


Authentication

Most queries are public. Four operations require a Bearer token: search_variant, the batch tools (cli_variant_search, cli_vcf_filter, file_upload, cli_variant_peek/peek), and create_dataset (which also needs the admin role). Contact agvd@afrigen-d.org for a token.

from agvd import AGVDClient

client = AGVDClient(token="YOUR_TOKEN")

Or set AGVD_TOKEN and omit token=:

export AGVD_TOKEN=YOUR_TOKEN
client = AGVDClient()  # picks up AGVD_TOKEN automatically

Public queries work with no token at all — AGVDClient() with nothing configured raises only when you call something that actually needs one, and does it immediately (no network round trip for a 401).


Quickstart

from agvd import AGVDClient

client = AGVDClient(token="YOUR_TOKEN")

# Look up a specific variant (accepts any common id delimiter — see
# "Variant ID format" below).
conn = client.fetch_variant(variant_id=["1:2929187:C:T"])
variant = conn[0]
print(variant.variant_id, variant.chromosome, variant.annotation.gene_name)

# Every gene-name match, across every page, as a simple iterator:
for variant in client.find_by_gene_all(["BRCA1", "TP53"]):
    print(variant.variant_id, [c.name for c in variant.clusters])

# Variants in a region with a population cluster above a MAF threshold:
for variant in client.filter_pos_maf_all("17", start=43044000, end=43125000, maf=0.05, great=True):
    print(variant.variant_id)

client.close()  # or use `with AGVDClient(...) as client:`

Pagination

Every query that can return many variants (fetch_variant, find_by_gene, find_by_position, search_variant, filter_pos_maf, filter_ids_maf, filter_gene_maf) returns one Relay page as a Connection:

conn = client.find_by_gene(["BRCA1"], first=50)
for variant in conn:                 # this page's variants
    ...
conn.total_count                      # matches, ignoring pagination
conn.page_info.has_next_page
next_page = client.find_by_gene(["BRCA1"], first=50, after=conn.page_info.end_cursor)

Each of those methods has an _all counterpart (find_by_gene_all, ...) that walks every page for you and yields Variant objects directly — that's what the quickstart above uses. Use the plain method when you only want one page, or just a count:

conn = client.find_by_gene(["BRCA1"], count_only=True)
conn.total_count   # no variant records fetched at all

Variant ID format

The database's internal variantID is stored underscore-joined: CHROMOSOME_POSITION_REFERENCE_ALTERNATE, e.g. 17_43044295_G_A. Every id-based lookup does an exact string match against that field — no normalisation happens server-side, so sending the wrong delimiter silently matches nothing (no error).

Every method on AGVDClient that takes a variant_id accepts the id in any common delimiter and normalises it for you — "17:43044295:G:A", "17-43044295-G-A", and "17_43044295_G_A" are all equivalent. If you're constructing ids yourself from parts, use agvd.build_variant_id:

from agvd import build_variant_id

vid = build_variant_id("chr17", 43044295, "g", "a")  # -> "17_43044295_G_A"

Note on cli_variant_peek: uniquely among these operations, the server echoes variant_id back dash-joined in the response (to match the website's URL convention) even though the input it matched against was underscore-joined. AGVDClient doesn't paper over this — the response model just carries whatever the server actually sent, with a docstring note on cli_variant_peek and VariantAvailability.


API reference

Method Returns Auth Notes
fetch_variant / _all Connection[Variant] — by variant_id and/or rs_id
find_by_gene / _all Connection[Variant] — by gene name(s)
find_by_position / _all Connection[Variant] — chromosome, optional exact position or range
search_variant / _all Connection[Variant] ✓ free-text, variant id or gene name
filter_pos_maf / _all Connection[Variant] — region + per-cluster MAF threshold
filter_ids_maf / _all Connection[Variant] — id list + per-cluster MAF threshold
filter_gene_maf / _all Connection[Variant] — gene(s) + per-cluster MAF threshold
cli_variant_search list[VariantThresholdResult] ✓ ≤500 ids; variantID only — see note below
cli_vcf_filter list[VariantThresholdResult] ✓ ≤500 ids; variantID or rsID
file_upload list[VariantThresholdResult] ✓ ≤500 ids; threshold accepted but ignored server-side
cli_variant_peek list[VariantAvailability] ✓ ≤500 ids; dataset required
peek list[VariantAvailability] ✓ convenience: classifies a mixed id list and calls cli_variant_peek for each kind
fetch_datasets list[Dataset] —
create_dataset Dataset ✓ (admin)
site_map_urls list[SiteMapURL] —

cli_variant_search only matches on variant_id — despite the underlying schema input type (VCFQueryInput) nominally accepting an rsID list too, the server resolver never reads it. Passing only rsIDs raises AGVDUserInputError before a network call. Use cli_vcf_filter (which does resolve rsIDs server-side) if you need rsID-based threshold lookups.

Every *_maf query's great argument means "keep clusters where maf >= maf" when True, maf <= maf when False/omitted.

dataset on most methods switches on the non-SAALS genotype merge (AGVDClient.NON_SAALS_DATASET / "AGVD_24A_Main_Non_SAALS") when set to that value — pass dataset= per call, or set a client-wide default: AGVDClient(token=..., dataset=agvd.NON_SAALS_DATASET).


Error handling

Every failure raises a subclass of agvd.AGVDError, carrying the server's (or a client-side) error code as .code:

from agvd import AGVDClient, AGVDUserInputError, AGVDRateLimitError, AGVDError

try:
    client.find_by_position("99")  # not a real chromosome
except AGVDUserInputError as exc:
    print(exc.code, exc)            # BAD_USER_INPUT ...
except AGVDRateLimitError as exc:
    print("retry after", exc.retry_after, "seconds")
except AGVDError as exc:            # catches anything else, including
    print(exc.code, exc)            # error codes this version doesn't know about yet
Exception Code Cause
AGVDUserInputError BAD_USER_INPUT Invalid argument (bad MAF/threshold, unknown chromosome, >500 ids, first out of 1–100, ...)
AGVDAuthenticationError UNAUTHENTICATED No/invalid/expired token
AGVDForbiddenError FORBIDDEN Valid token, wrong role (e.g. non-admin calling create_dataset)
AGVDQueryTooComplexError QUERY_TOO_COMPLEX Query exceeded the server's complexity budget
AGVDRateLimitError RATE_LIMITED HTTP 429 — 200 requests / 15 min per IP
AGVDServerError INTERNAL_SERVER_ERROR 5xx or a server-side DB error
AGVDNetworkError NETWORK_ERROR / TIMEOUT Never got a response

5xx responses, network errors, and GraphQL INTERNAL_SERVER_ERROR are retried automatically with exponential backoff (max_retries=, default 3); 429s wait out Retry-After; everything else (4xx, validation errors) fails immediately — retrying a deterministic rejection just wastes quota.


The agvd CLI

The CLI (pip install "agvd[cli]") reads a VCF, CSV, TSV, or Excel file of variants, submits them in parallel batches to cli_variant_search, and writes an annotated copy plus a JSON summary. It's a thin wrapper over AGVDClient — see above if you'd rather call the API directly from Python.

agvd --KEY YOUR_TOKEN --INFILE variants.vcf --OUTPUT results.csv --THRESHOLD 0.05

From a CSV/TSV/Excel file with a variant ID column

agvd --KEY YOUR_TOKEN --INFILE variants.csv --COLUMN variant_id --OUTPUT results.csv --THRESHOLD 0.05

From a table with separate chromosome/position columns

agvd --KEY YOUR_TOKEN --INFILE variants.tsv --CHR chrom --POS position --REF ref --ALT alt --OUTPUT results.tsv --THRESHOLD 0.01

Arguments

Argument Short Description
--KEY -k API token (or set AGVD_TOKEN)
--INFILE -i Input file (.vcf, .vcf.gz, .csv, .tsv, .xlsx)
--OUTPUT -o Output file path (.csv, .tsv, or .xlsx)
--THRESHOLD -t MAF threshold for filtering (e.g. 0.05)
--DATASET -d Dataset to query (default: AGVD_24A_Main_Non_SAALS)
--BATCH -b Variants per API request, 1–500 (default: 500)
--COLUMN -c Column containing variant IDs (CSV/TSV/Excel)
--CHR / --POS / --REF / --ALT Separate chromosome/position/ref/alt column names
--base-url Override the GraphQL endpoint
--threads Parallel worker threads (default: 4)
--cache Cache API responses in-process (LRU, up to 5000 batches)
--dry-run Parse and validate ids without making API calls
--verbose Debug-level logging
--peek Quick availability check — see below
--version Print the installed version and exit

Only variant_id-style rows go to the batch MAF lookup (cliVariantSearch doesn't support rsID — see the API reference above); rsID rows are marked UNSUPPORTED in the output rather than silently dropped.

Peek mode

Checks availability (and, for the website, a direct link) for each id, without a threshold comparison. Accepts a mix of rsIDs and variant ids:

agvd --KEY YOUR_TOKEN --peek rs334 17:43044295:G:A
# or from a file, one id per line:
agvd --KEY YOUR_TOKEN --peek --INFILE variants.txt

Output

The output file keeps all original columns and adds:

Column Description
AGVDCUTOFF ABOVE, BELOW, NO MATCH, INVALID, UNSUPPORTED (rsID — ...), or ERROR
African_MAF MAF value returned by the API
THRESHOLD Threshold actually applied
<ClusterName>_MAF Per-population cluster MAF (one column per cluster)

A companion <output>_summary.json is written alongside it:

{ "total": 1000, "successful": 998, "failed": 2, "success_rate": 0.998 }

Development

pip install -e ".[dev]"
pytest

The package lives under src/agvd/ (an installed-package / src layout, not a flat set of top-level modules) — install it (pip install -e .) rather than running from the repo root, or rely on pythonpath = ["src"] in pyproject.toml, which pytest already picks up.


License

MIT License — see LICENSE for details.


Contact

For support or to request an AGVD API token: agvd@afrigen-d.org

Metadata

Release files for agvd 0.1.3

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

Source distribution (sdist)

Source distribution for agvd 0.1.3
File Size Uploaded Provenance Actions
agvd-0.1.3.tar.gz 39.9 kB Without provenance Details for agvd-0.1.3.tar.gz

Built distribution (wheel)

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

Total release size: 70.8 kB

Release files / agvd-0.1.3.tar.gz

Download URL agvd-0.1.3.tar.gz
Size 39.9 kB
Tags Source
SHA-256 checksum
How to use checksums
81269496c3b81e2e6f9acfa6f2dfa1e2b480903ad1b2b17ef773ba348ee40b4f
BLAKE2b-256 checksum
How to use checksums
e4213bacfe19bca4863b2bfc31c08f9410ea236a37b790b025dae1646129da0b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.1

Release files / agvd-0.1.3-py3-none-any.whl

Download URL agvd-0.1.3-py3-none-any.whl
Size 30.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
1518a456233bbd8d34a93637ccbd9994e611653432217e8f023cf5419fc5e5be
BLAKE2b-256 checksum
How to use checksums
acc58518f7f86f6f570890a7388b45409c7e28c1a0f06923615b1016012dc303
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.1

Security for agvd 0.1.3

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.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

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