KeyCall
One consistent interface for validating AI-provider API keys, listing and filtering the models available to them, and making normalized calls, so every product stops rebuilding the same model-picker filters and provider wrappers.
Status: early release. Key validation, model listing and filtering, text generation, native web search with normalized citations, and structured JSON output all work and are live-verified against every supported provider. Streaming, general tool calling, and non-text modalities are not implemented yet. The API is settled but may still shift before 1.0.
Docs: USAGE.md for the full API and CLI reference · ARCHITECTURE.md for the layer diagram and component contracts · CHANGELOG.md for version history.
Quick start
from keycall import KeyCall, Message, ModelCategory, TextInput
with KeyCall(provider="openai", api_key=secret) as client:
discovery = client.list_models(categories={ModelCategory.TEXT_GENERATION})
result = client.generate_text(
model=discovery.models[0].id,
messages=[Message(role="user", content=[TextInput(text="Hello.")])],
)
print(result.text)
print(result.usage.total_tokens)
print(result.round_trip_duration_ms)
- Explicit provider, always. KeyCall never guesses which vendor issued a key and never sends a credential to more than the one provider you name.
- No credential storage. Keys live in memory for the client's lifetime, wrapped in a redacting type that keeps them out of reprs, logs, traces, exceptions, and pickles. Your app decides how to store them.
- Model filtering built in. Text-generation models by default; embeddings, image, audio, and other categories on request; unknown models never silently enter the default picker.
- Typed errors. Invalid key, rate limit, provider outage, timeout, and malformed response are distinguishable, never collapsed into "invalid key."
- Web search with citations.
web_search=Trueturns on the provider's native search tool (OpenAI, Anthropic, Gemini; Perplexity always searches) and returns sources normalized to oneCitationshape. - Structured output.
response_schema=<JSON Schema>is enforced provider-side on OpenAI, Anthropic, Gemini, Moonshot, and Perplexity; on providers without enforcement (DeepSeek, unverified custom targets) KeyCall falls back to guaranteed-valid-JSON mode and adds a result warning rather than claiming a guarantee it can't back.result.textis always the JSON string, regardless of which mechanism produced it. - Hardened transport. TLS always verified, redirects refused, response sizes capped, SSRF and DNS-rebinding guards on custom endpoints, and generation is never silently retried.
Provider support
Live-verified 2026-08-05 (one model-list call plus one bounded generation per provider):
| Provider | Protocol | Listing | Generation |
|---|---|---|---|
| OpenAI | openai | verified | verified |
| Anthropic | anthropic | verified | verified |
| Google Gemini | gemini | verified | verified |
| DeepSeek | openai-compatible | verified | verified |
| Perplexity | openai-compatible | verified | verified |
| Moonshot/Kimi | openai-compatible | verified | verified |
Custom endpoint (explicit base_url) |
openai-compatible | fixtures only | fixtures only |
Two provider quirks worth knowing, both handled:
Gemini keeps retired models in its list endpoint (gemini-2.5-flash returns
"no longer available to new users") with no lifecycle field to pre-filter on,
and meters quota per model and tier, so one model's 429 says nothing about the
next. Its supportedGenerationMethods is also a transport signal rather than a
modality claim: TTS variants advertise generateContent and then refuse a text
response, so KeyCall lets a distinctive identifier modality outrank it.
Perplexity's GET /v1/models is scoped to the Agent API and returns
vendor-prefixed router models (anthropic/..., perplexity/sonar) that the
Sonar route rejects. Sonar's own models are not API-discoverable, so KeyCall
maintains them in its catalog and uses the list call purely as a credential
check.
Structured output notes, per provider
- OpenAI requires
additionalProperties: falseon every object level of the schema for its strictjson_schemamode, or the request 400s. This is an OpenAI requirement, not a KeyCall one — write schemas with it from the start. - Anthropic implements structured output by forcing a single synthetic
tool call; it cannot be combined with
web_search=Truein the same request (forcing one tool prevents the model calling a different one), and KeyCall rejects that combination before any network call. - Gemini's equivalent combination (
web_search=Truewithresponse_schema) is not gated — no live-verified evidence either way that Gemini rejects it, so KeyCall passes it through rather than guessing. - DeepSeek hard-requires the literal word "json" somewhere in the prompt
for its
json_objectfallback mode, or it 400s. KeyCall detects this and injects a short system instruction automatically when needed, and always says so via a result warning. - Moonshot/Kimi reasoning-capable models can spend the entire
max_output_tokensbudget on a visible reasoning trace and never emit a final answer if the budget is too small. KeyCall detects the resulting empty-content-with-reasoning-trace response and adds a warning rather than returning a silent empty result; give these models a larger budget than you'd expect a short answer to need.
Because of quirks like these, keycall verify --generate walks the filtered
models in provider order and prints the outcome of every attempt until one
succeeds, so drift stays visible rather than being masked by a silent retry.
Local viewer
keycall view --source ./keys.toml
Opens a token-protected local web app over your loaded targets: a dashboard with live key checks, a sortable model browser with category filters, a playground for real generation calls (web search included), and a verify report. Keys never leave the server process and never appear in the browser.
Or double-click / run a launcher from a fresh clone — it creates the venv,
installs KeyCall, finds your key file, and starts the viewer:
launch.command (macOS), launch.sh (Linux/macOS), launch.bat (Windows).
Verifying keys from the command line
keycall verify --source ./keys.toml
keycall verify --source ./keys.toml --generate
--generate also makes one small bounded call per target. Sources can be TXT,
JSON, or TOML, an explicit env:VAR_NAME reference, or an interactive prompt.
See keycall-test-keys.example.toml for the format and
USAGE.md for the full reference. Keys never appear
in output, and KeyCall never writes to or deletes your credential file.
Installation
pip install keycall
Development
pip install -e ".[dev]"
pytest
Author
Built by Mo Shehu.
License
AGPL-3.0-or-later. 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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file keycall-0.4.0.tar.gz.
File metadata
- Download URL: keycall-0.4.0.tar.gz
- Upload date:
- Size: 105.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
dfbeacd6766eb714d72a5fbd7d785bee54290d290e86691250190b04d3443ab7
|
|
| MD5 |
4d0ff2283598132dea95d63298abfd32
|
|
| BLAKE2b-256 |
369ef8f7cb9acb44ea119ead98fde01d9ddf67b9cd2edd371b18b792a7e291da
|
Provenance
The following attestation bundles were made for keycall-0.4.0.tar.gz:
Publisher:
release.yml on shehuphd/keycall
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
keycall-0.4.0.tar.gz -
Subject digest:
dfbeacd6766eb714d72a5fbd7d785bee54290d290e86691250190b04d3443ab7 - Sigstore transparency entry: 2380427658
- Sigstore integration time:
-
Permalink:
shehuphd/keycall@71895f93334bb2d15fa0b0db8bf5fbfb6bc50e07 -
Branch / Tag:
refs/tags/v0.4.0 - Owner: https://github.com/shehuphd
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@71895f93334bb2d15fa0b0db8bf5fbfb6bc50e07 -
Trigger Event:
push
-
Statement type:
File details
Details for the file keycall-0.4.0-py3-none-any.whl.
File metadata
- Download URL: keycall-0.4.0-py3-none-any.whl
- Upload date:
- Size: 85.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4acc4708e665a58aa811da03f5b6f8680b1e3b34efbfb0765895b3476e0da5d6
|
|
| MD5 |
0446c494adc60ba43dcc1ce463025919
|
|
| BLAKE2b-256 |
7e326fa5c89151e8b89ea49f312a9693f9ec2ef31fc9ea7f065e9b2cec66662c
|
Provenance
The following attestation bundles were made for keycall-0.4.0-py3-none-any.whl:
Publisher:
release.yml on shehuphd/keycall
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
keycall-0.4.0-py3-none-any.whl -
Subject digest:
4acc4708e665a58aa811da03f5b6f8680b1e3b34efbfb0765895b3476e0da5d6 - Sigstore transparency entry: 2380427736
- Sigstore integration time:
-
Permalink:
shehuphd/keycall@71895f93334bb2d15fa0b0db8bf5fbfb6bc50e07 -
Branch / Tag:
refs/tags/v0.4.0 - Owner: https://github.com/shehuphd
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@71895f93334bb2d15fa0b0db8bf5fbfb6bc50e07 -
Trigger Event:
push
-
Statement type: