Skip to main content

jintel

Python client for the Jintel intelligence API. Typed queries, Pydantic-validated responses, an optional response cache, and a dynamic query builder that threads per-field filters into the underlying GraphQL.

Mirrors @yojinhq/jintel-client section-for-section.

Install

pip install jintel

Requires Python 3.10+.

Quick start

import os
from jintel import JintelClient

with JintelClient(api_key=os.environ["JINTEL_API_KEY"]) as jintel:
    quotes = jintel.quotes(["AAPL", "MSFT", "BTC"])
    aapl = jintel.enrich_entity("AAPL", fields=["market", "news", "analyst"])

Async twin — same surface, await-able:

import asyncio, os
from jintel import AsyncJintelClient

async def main() -> None:
    async with AsyncJintelClient(api_key=os.environ["JINTEL_API_KEY"]) as jintel:
        quotes = await jintel.quotes(["AAPL"])

asyncio.run(main())

All public methods return JintelResult[T] — a discriminated union Ok[T] | Err — so errors never throw. Pattern-match instead of try/except:

from jintel import Ok, Err

result = jintel.quotes(["AAPL"])
match result:
    case Ok(data=quotes):
        for q in quotes:
            if q is None:
                continue  # PIT UNSUPPORTED slot
            print(q.ticker, q.price)
    case Err(error=msg):
        log.warning("quotes failed: %s", msg)

For arbitrary GraphQL, use jintel.request(query, variables) (raises) or jintel.raw_request(query, variables) (returns the full envelope including extensions).

Filtering array sub-graphs

Most array fields accept an optional dedicated filter. The generic ArrayFilterInput covers date range + limit + sort; many sub-graphs have domain-specific inputs with extra dimensions.

from jintel import EnrichOptions
from jintel.filters import (
    ArrayFilterInput, NewsFilterInput, ExecutivesFilterInput,
    InsiderTradeFilterInput, EarningsFilterInput, SegmentRevenueFilterInput,
    TopHoldersFilterInput, FinancialStatementFilterInput,
    SanctionsFilterInput, CampaignFinanceFilterInput, FilingsFilterInput,
    RiskSignalFilterInput, OptionsChainFilterInput, FuturesCurveFilterInput,
    SortDirection,
)
from jintel.types import (
    AcquisitionDirection, ExecutiveSort, FilingType, OptionsChainSort,
    OptionType, RiskSignalType, Severity,
)

# Generic ArrayFilterInput — research, predictions, discussions, social,
# institutionalHoldings, earningsPressReleases, periodicFilings,
# market.history/keyEvents/shortInterest, economics.
jintel.enrich_entity("AAPL", fields=["research", "market"], options=EnrichOptions(
    filter=ArrayFilterInput(since="2025-01-01", limit=10, sort=SortDirection.DESC),
))

# NewsFilterInput — filter by source and sentiment score
jintel.enrich_entity("AAPL", fields=["news"], options=EnrichOptions(
    news_filter=NewsFilterInput(min_sentiment=0, limit=10),
))

# ExecutivesFilterInput — top-paid officers only
jintel.enrich_entity("AAPL", fields=["executives"], options=EnrichOptions(
    executives_filter=ExecutivesFilterInput(
        min_pay=1_000_000, sort_by=ExecutiveSort.PAY_DESC, limit=5
    ),
))

# InsiderTradeFilterInput — directors only, acquisitions >= $100k
jintel.enrich_entity("AAPL", fields=["insiderTrades"], options=EnrichOptions(
    insider_trades_filter=InsiderTradeFilterInput(
        is_director=True,
        acquired_disposed=AcquisitionDirection.ACQUIRED,
        min_value=100_000,
    ),
))

# EarningsFilterInput — reported beats >= 5%
jintel.enrich_entity("AAPL", fields=["earnings"], options=EnrichOptions(
    earnings_filter=EarningsFilterInput(only_reported=True, min_surprise_percent=5),
))

# SegmentRevenueFilterInput — product breakdown >= $1B
jintel.enrich_entity("AAPL", fields=["segmentedRevenue"], options=EnrichOptions(
    segmented_revenue_filter=SegmentRevenueFilterInput(
        dimensions=["PRODUCT"], min_value=1_000_000_000
    ),
))

# TopHoldersFilterInput — paginated top-holder lookup
jintel.enrich_entity("AAPL", fields=["topHolders"], options=EnrichOptions(
    top_holders_filter=TopHoldersFilterInput(limit=25, offset=0, min_value=50_000),
))

# FinancialStatementFilterInput — annual only
jintel.enrich_entity("AAPL", fields=["financials"], options=EnrichOptions(
    financial_statements_filter=FinancialStatementFilterInput(
        period_types=["12M"], limit=5
    ),
))

# SanctionsFilterInput + CampaignFinanceFilterInput — on regulatory
jintel.enrich_entity("Gazprom", fields=["regulatory"], options=EnrichOptions(
    sanctions_filter=SanctionsFilterInput(min_score=80, programs=["SDGT"]),
    campaign_finance_filter=CampaignFinanceFilterInput(cycle=2024, party="DEM"),
))

# FilingsFilterInput — narrow SEC filings by form type
jintel.enrich_entity("AAPL", fields=["regulatory"], options=EnrichOptions(
    filings_filter=FilingsFilterInput(
        types=[FilingType.FILING_10K, FilingType.FILING_10Q], limit=5
    ),
))

# RiskSignalFilterInput — drop low-severity noise
jintel.enrich_entity("Gazprom", fields=["risk"], options=EnrichOptions(
    risk_signal_filter=RiskSignalFilterInput(severities=[Severity.HIGH, Severity.CRITICAL]),
))

# OptionsChainFilterInput — chains can exceed 5 000 rows; filter aggressively
jintel.enrich_entity("BTC", fields=["derivatives"], options=EnrichOptions(
    options_filter=OptionsChainFilterInput(
        option_type=OptionType.CALL,
        strike_min=60_000, strike_max=80_000,
        min_open_interest=100,
        sort=OptionsChainSort.VOLUME_DESC,
        limit=25,
    ),
))

# FuturesCurveFilterInput — defaults to ASC (nearest contract first)
jintel.enrich_entity("BTC", fields=["derivatives"], options=EnrichOptions(
    futures_filter=FuturesCurveFilterInput(limit=10),
))

Each filter option applies to one sub-graph only, so you can mix them in a single request. The generic filter no longer applies to fields that migrated to domain-specific inputs (news, executives, insiderTrades, earnings, segmentedRevenue, topHolders, financials.*, regulatory.sanctions, regulatory.campaignFinance) — use the dedicated option for those, or the client raises JintelValidationError at call time.

Top-level queries

Economics and short-interest queries accept the generic ArrayFilterInput. sanctions_screen and campaign_finance accept their domain-specific filters:

from jintel.types import GdpType
from jintel.filters import ArrayFilterInput, SanctionsFilterInput, CampaignFinanceFilterInput

jintel.gdp("USA", type=GdpType.REAL,
           filter=ArrayFilterInput(since="2010-01-01", limit=20))
jintel.inflation("USA", filter=ArrayFilterInput(since="2020-01-01"))
jintel.short_interest("GME", filter=ArrayFilterInput(limit=5))

# Root sanctions screen — filter by score, list, or program
jintel.sanctions_screen(
    "Gazprom", country="RU",
    filter=SanctionsFilterInput(min_score=80, list_names=["SDN"]),
)

# Root campaign finance — narrow to party / state / cycle
jintel.campaign_finance(
    "Acme PAC", cycle=2024,
    filter=CampaignFinanceFilterInput(party="DEM", state="CA", min_raised=100_000),
)

# US macro economic series
jintel.macro_series("UNRATE",
                    filter=ArrayFilterInput(since="2000-01-01", limit=300))
jintel.macro_series_batch(["GDPC1", "CPIAUCSL"],
                          filter=ArrayFilterInput(since="2010-01-01"))

Defaults

Filter Default limit Default sort
ArrayFilterInput 20 DESC
NewsFilterInput 20 DESC (by date)
ExecutivesFilterInput 20 PAY_DESC
InsiderTradeFilterInput 20 DESC (by transactionDate)
EarningsFilterInput 20 DESC (by reportDate)
SegmentRevenueFilterInput 20 DESC (by filingDate)
TopHoldersFilterInput 20 (offset 0) DESC (by value)
FinancialStatementFilterInput 20 DESC (by periodEnding)
SanctionsFilterInput 20 DESC (by score)
CampaignFinanceFilterInput 20 DESC (by totalRaised)
FilingsFilterInput 20 DESC
RiskSignalFilterInput 20 DESC
FuturesCurveFilterInput 50 ASC
OptionsChainFilterInput 100 EXPIRATION_ASC
ClinicalTrialFilterInput 20 DESC (by startDate)
FdaEventFilterInput 20 DESC (by reportDate)
LitigationFilterInput 20 DESC (by dateFiled)
GovernmentContractFilterInput 20 DESC (by actionDate)

Omitting a filter on a sub-graph returns the full upstream set with that input's defaults applied.

Breaking changes in 0.21

  • top_holders: { limit, offset } option removed. Use top_holders_filter=TopHoldersFilterInput(...) instead.
  • The generic filter option no longer threads into news, insiderTrades, earnings, segmentedRevenue, or financials.* — use the new domain-specific filter options above. The Python client raises JintelValidationError at call time if you mix them.

Batch enrichment

batch_enrich accepts up to 20 tickers and pushes server-side loaders to batch and deduplicate upstream calls:

batch = jintel.batch_enrich(
    ["AAPL", "MSFT", "GOOG"],
    fields=["market", "news", "technicals"],
    options=EnrichOptions(filter=ArrayFilterInput(limit=5)),
)

Point-in-time queries (as_of)

Pass as_of (ISO 8601) to bound a query to data that was knowable at that timestamp — no lookahead bias in backtests. Set it per call or as a client-wide default.

# Per-call — overrides any default.
aapl_last_summer = jintel.batch_enrich(
    ["AAPL"],
    fields=["news", "institutionalHoldings"],
    options=EnrichOptions(
        as_of="2023-08-15T00:00:00Z",
        filter=ArrayFilterInput(limit=5),
    ),
)

# Or once at construction — locks the entire client to a replay date.
replay = JintelClient(
    api_key=os.environ["JINTEL_API_KEY"],
    as_of="2023-08-15T00:00:00Z",
)

Every PIT response carries extensions.asOf.fields with the per-field policy:

from jintel import parse_as_of_extension

envelope = jintel.raw_request(
    """query Q($t: [String!]!, $a: String) {
         quotes(tickers: $t, asOf: $a) { ticker }
       }""",
    {"t": ["AAPL"], "a": "2023-08-15T00:00:00Z"},
)
ext = parse_as_of_extension(envelope)
if ext is not None:
    for path, policy in ext.fields.items():
        print(path, policy.klass)
# Entity.market.quote → AsOfFieldClass.UNSUPPORTED

SUPPORTED fields honor as_of honestly. BEST_EFFORT fields run with a documented caveat. UNSUPPORTED fields (live quotes, current fundamentals, OFAC SDN, derivatives, etc.) return null/[] rather than serve current data — quotes() returns list[MarketQuote | None] so callers must handle the gap. Cached responses are bucketed by as_of, so PIT and live requests never share a slot.

Response caching

Pass cache=True to enable an in-process TTL cache (30 s for quotes, 5 min for enrich / price history). Eliminates redundant HTTP when the same data is requested in a short window.

from jintel import JintelClient, CacheConfig

jintel = JintelClient(
    api_key=os.environ["JINTEL_API_KEY"],
    cache=CacheConfig(quotes_ttl_ms=15_000, enrich_ttl_ms=120_000),
)

jintel.invalidate_cache(["AAPL"])  # after an external signal event
print(jintel.cache_stats())  # CacheStats(hits=12, misses=3, evictions=0, size=15)

The cache is in-memory by default. To plug a Redis-backed implementation, satisfy the CacheBackend Protocol and pass it as cache_backend=... (the SDK does not ship a Redis backend in v0).

Payment modes (Bearer + x402)

Two ways to pay for queries on the same endpoint:

Bearer (prepaid credits) — pass api_key and you're done. This is the default mode and matches the TS client.

x402 (per-call USDC on Base) — pass a payment_signer callable. On any 402 Payment Required response, the SDK decodes the PAYMENT-REQUIRED header into a typed X402Quote, calls your signer to produce a base64 EIP-3009 USDC authorization, retries the request with PAYMENT-SIGNATURE, then exposes the PAYMENT-RESPONSE settlement record on client.last_settlement.

from jintel import JintelClient, X402Quote

def my_signer(quote: X402Quote) -> str:
    # Use eth-account, web3.py, or an MCP wallet — produce a base64 PaymentPayload.
    ...

# x402-only — no Bearer key needed
with JintelClient(payment_signer=my_signer) as jintel:
    result = jintel.quotes(["AAPL"])
    print(jintel.last_settlement)  # X402SettlementResponse | None

# Both modes — Bearer settles by default; x402 fires on CREDITS_EXHAUSTED
with JintelClient(api_key=os.environ["JINTEL_API_KEY"], payment_signer=my_signer) as jintel:
    ...

When you don't supply a signer, a 402 raises JintelPaymentRequiredError(quote: X402Quote) so callers can handle the payment manually. When the Bearer path runs out of credits, the server returns the GraphQL error code CREDITS_EXHAUSTED, which the SDK surfaces as JintelCreditsExhaustedError.

This SDK does not embed wallet logic — payment_signer is the integration seam. See examples/x402_payment.py for a worked example using eth-account.

Pattern-matching JintelResult

Python's match/case makes the discriminated union ergonomic — Python-specific addition over the TS surface:

from jintel import Ok, Err

match jintel.gdp("USA"):
    case Ok(data=points):
        latest = points[0]
        print(f"GDP {latest.date}: {latest.value}")
    case Err(error=msg):
        print(f"GDP query failed: {msg}")

.unwrap() returns the data or raises JintelError. .map(fn) transforms the success branch. .value_or(default) returns the data or a fallback.

License

MIT

Metadata

Release files for jintel 0.29.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 jintel 0.29.0
File Size Uploaded Provenance Actions
jintel-0.29.0.tar.gz 40.4 kB With provenance Details for jintel-0.29.0.tar.gz

Built distribution (wheel)

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

Total release size: 84.5 kB

Release files / jintel-0.29.0.tar.gz

Download URL jintel-0.29.0.tar.gz
Size 40.4 kB
Tags Source
SHA-256 checksum
How to use checksums
eeaf29dd0290591734a0020bf78f5a5f18cad2d860dd043c1ae4fc2308328c7a
BLAKE2b-256 checksum
How to use checksums
6249d78fa2ee5fe9d2b7d0ebab03ccd92a58d2112ba8347d3f0047d7c62964e5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

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 Apr 27, 2026.

Transparency log

Release files / jintel-0.29.0-py3-none-any.whl

Download URL jintel-0.29.0-py3-none-any.whl
Size 44.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
97e264023229e2ac4b5ee0793b5a3d3eaade1a23b3aa3e05fe34fe864df537ee
BLAKE2b-256 checksum
How to use checksums
616055ffdfe629f6aa5f29c0c0c95c8ed23319d0463868dd931dc79ec6ea7e27
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

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 Apr 27, 2026.

Transparency log

Security for jintel 0.29.0

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.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for jintel-0.29.0.tar.gz

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 Apr 27, 2026.

Transparency log

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.

Release history Release notifications | RSS feed

This release

0.29.0 This release

2 release files Full provenance

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