Using connectors¶
This guide collects the practical patterns for working with connectors in
application or notebook code: choosing which providers to load, navigating the
Connectors collection, running the search-then-fetch loop, reading a Result,
binding credentials, pointing at a custom catalog, and handling typed errors.
For the underlying model see the connector contract and discovery and catalogs. For the list of installed providers and their connectors, see the provider reference.
Selecting and loading providers¶
Loading is done through the kernel's discover module. You rarely import a
provider package directly.
Load everything installed (forgiving — skips and logs any provider that fails to import):
Load a named subset (strict — raises LookupError if a name is not installed):
Use the strict form when you depend on specific providers and want a clear
failure if one is missing from the environment. Use load_all() when you want
whatever the deployment happens to have installed.
You can also enumerate what is installed without importing any provider module:
from parsimony import discover
for provider in discover.iter_providers():
print(provider.name, provider.version, provider.homepage)
The Connectors collection API¶
discover.load and discover.load_all both return an immutable Connectors
collection. Every mutating-looking operation returns a new collection.
Index by name. Indexing is name-keyed, not positional:
connectors.get("fred_fetch") returns the connector or None instead of
raising. "fred_fetch" in connectors tests membership.
List names, length, iterate:
connectors.names() # sorted list of connector names
len(connectors) # number of connectors
for c in connectors: # iterate over Connector objects
print(c.name)
Merge two collections with +:
from parsimony import discover
macro = discover.load("fred")
markets = discover.load("treasury")
both = macro + markets
Bind parameter values across the collection (see Binding credentials below):
Filter by tag. Connectors carry free-form tags (such as domain labels like
macro):
Search then fetch¶
Both discovery models follow the same two-step shape — find an identifier, then fetch by it — but they differ in where the search runs.
Catalog-backed providers¶
Catalog-backed providers (such as treasury, riksbank, sdmx, bls) ship a
prebuilt searchable catalog. The search connector is named <provider>_search
and takes query, an optional limit, and an optional catalog_url. It
returns a code (KEY) column you pass to the fetch connector.
from parsimony import discover
connectors = discover.load("riksbank")
hits = connectors["riksbank_search"](query="EUR SEK exchange rate", limit=5)
print(hits.frame[["code", "title", "score"]])
# Take a code from the search results and fetch it.
code = hits.frame.iloc[0]["code"]
result = connectors["riksbank_fetch"](series_id=code)
print(result.frame.head())
The catalog snapshot loads lazily on first search and is cached under
~/.cache/parsimony/catalogs/. Subsequent searches reuse the cache.
Native-search providers¶
Native-search providers (such as fred, finnhub, fmp, sec_edgar) wrap the
provider's own search endpoint instead of shipping a catalog. There is no
snapshot to download; each search is a live call. Every search connector takes its
free text as query=; the identifier column it returns is provider-specific (for
FRED it is id).
from parsimony import discover
connectors = discover.load("fred")
hits = connectors["fred_search"](query="US unemployment rate")
series_id = hits.frame.iloc[0]["id"]
result = connectors["fred_fetch"](series_id=series_id)
Consult the provider reference for each provider's search parameter and identifier column.
Reading a Result¶
Calling any connector returns a Result. How you read it depends on the
payload.
Tabular payloads (most connectors) expose a pandas DataFrame:
result = connectors["fred_fetch"](series_id="UNRATE")
result.frame # the DataFrame; raises TypeError if the payload isn't tabular
result.raw # the raw payload (here, the same DataFrame)
result.is_tabular # True
Dict or scalar payloads are read through result.raw:
Provenance is attached to every result and is framework-built:
result.provenance.source # connector name, e.g. 'fred_fetch'
result.provenance.fetched_at # datetime in UTC
result.provenance.params # the call-time parameters (secrets stripped)
A bounded view for LLM context. to_llm() renders a governed,
size-bounded string view — honest row/column counts, a per-column schema, and
the first rows of a table (or a structural preview for opaque payloads). This is
what an agent sees, and it is useful for logging too:
print(result.to_llm())
# Result (table): 900 rows × 7 columns
# Columns:
# - series_id: object (KEY ns:fred)
# - title: object (TITLE)
# ...
# Rows (showing 10 of 900):
# series_id,title,...
Binding credentials for operator use¶
Keyed providers accept an api_key="" parameter and fall back to a
<PROVIDER>_API_KEY environment variable. For local work the environment
variable is enough. When an operator wants the key fixed and kept off the
connector's call surface — for example before handing connectors to an agent —
bind it:
from parsimony import discover
connectors = discover.load("fred").bind(api_key="your-key-here")
# api_key is bound; it is no longer an exposed parameter and is never
# recorded in provenance.
result = connectors["fred_fetch"](series_id="UNRATE")
bind returns a new collection and only touches connectors that actually accept
the named parameter, so binding api_key across a mixed collection leaves
keyless connectors untouched. To see which environment variables a bundle needs
before you run it, call connectors.env_vars() — it returns the union of every
connector's declared requires= (the env vars a call needs to succeed), without
reading os.environ:
connectors = discover.load_all()
print(connectors.env_vars()) # e.g. frozenset({"FRED_API_KEY", "SEC_EDGAR_USER_AGENT"})
Each keyed provider package also exports a
load(*, api_key=...) helper that does the bind for you:
See credentials for the full model, including why binding keeps the key away from an agent surface.
Pointing at a custom catalog¶
Each catalog-backed provider reads its catalog snapshot from a default Hugging
Face URL, but you can override it with the environment variable
PARSIMONY_<PROVIDER>_CATALOG_URL. This is useful for pinning to a mirrored or
self-hosted snapshot, or for testing against a locally built catalog.
You can also override per call with the search connector's catalog_url
parameter, which takes precedence over the environment variable:
result = connectors["treasury_search"](
query="federal debt outstanding",
catalog_url="hf://your-org/treasury-mirror",
)
A keyless catalog-backed package's load helper accepts the same override:
The resolution order is: explicit catalog_url argument, then the
PARSIMONY_<PROVIDER>_CATALOG_URL environment variable, then the published
default.
Handling typed errors¶
Connectors raise typed exceptions from parsimony.errors rather than returning
error sentinels. Every exception carries a provider attribute, and the message
is an agent-facing string. Catch the base class ConnectorError to handle any
connector failure, or catch specific subclasses for tailored handling.
from parsimony import discover
from parsimony.errors import (
UnauthorizedError,
RateLimitError,
EmptyDataError,
ConnectorError,
)
connectors = discover.load("fred")
try:
result = connectors["fred_fetch"](series_id="UNRATE")
except UnauthorizedError as exc:
# Missing or invalid key — exc names the expected env var.
print(f"auth failed for {exc.provider}: {exc}")
except RateLimitError:
# Back off and retry later.
print("rate limited; retry after a delay")
except EmptyDataError:
# The parameters were valid but returned no rows.
print("no data for those parameters")
except ConnectorError as exc:
# Any other connector failure (ProviderError, ParseError,
# InvalidParameterError, CatalogNotFoundError, ...).
print(f"{exc.provider} error: {exc}")
The full set is UnauthorizedError, PaymentRequiredError, RateLimitError,
ProviderError, EmptyDataError, ParseError, InvalidParameterError, and
CatalogNotFoundError. See errors for what each one means
and when a connector raises it.