Ravn from Python, with requests
POST JSON to https://api.askravn.com/v1/tools/<tool> with requests and a Bearer header, then branch on the status field: ok carries results, not_found is a free miss, and a 429 tells you how long to wait in its Retry-After header.
Before you write code
- / 01
Install requests
pip install requests. No Ravn SDK is needed; the API is plain JSON over HTTPS. - / 02
Put the key in the environment
export RAVN_API_KEY=rw_live_…locally, or your platform’s secret store in production. Never hard-code it. - / 03
Read the schemas
GET https://api.askravn.com/v1/toolsreturns every tool with its JSON Schema, so you can validate inputs before sending. - / 04
Paste the client below
It handles the three cases that matter: data, a free miss, and a wait.
ravn_client.py
import os
import time
import requests
API = "https://api.askravn.com/v1/tools/"
SESSION = requests.Session()
SESSION.headers["Authorization"] = "Bearer " + os.environ["RAVN_API_KEY"]
class RavnError(Exception):
def __init__(self, http, code, message):
super().__init__(f"{http} {code}: {message}")
self.http, self.code = http, code
def call(tool, payload, max_wait=300, tries=4):
"""Returns results dict/list, or None on a free not_found miss."""
for attempt in range(tries):
r = SESSION.post(API + tool, json=payload, timeout=60)
if r.status_code == 429:
wait = int(r.headers.get("Retry-After", "30"))
if wait > max_wait: # 5-hour window is full: don't sleep for hours
raise RavnError(429, r.json()["error"]["code"], f"retry in {wait}s")
time.sleep(wait)
continue
if r.status_code == 503 and attempt < tries - 1:
time.sleep(2 ** attempt)
continue
body = r.json()
if r.status_code != 200:
err = body["error"]
raise RavnError(r.status_code, err["code"], err["message"])
return body["results"] if body["status"] == "ok" else None
raise RavnError(r.status_code, "gave_up", "retries exhausted")
if __name__ == "__main__":
v = call("validate_email", {"email": "jane@acme.com"})
if v is None:
print("no verdict")
elif v["status"] == "valid" and v["risk_score"] in ("A", "B"):
print("send")
elif v["status"] == "risky":
print("review:", v["reason"]) # e.g. catch_all_domain
else:
print("drop:", v["reason"])
for v in call("validate_emails", {"emails": ["a@acme.com", "b@acme.com"]}) or []:
print(v["email"], v["status"], v["risk_score"])Patterns that hold up
- / 01
Batch, don’t loop
For lists, send up to 1,000 addresses to
validate_emailsper call. Duplicates are counted once, and you make far fewer requests. - / 02
Connect a provider key for people calls
people search and other people, company and carrier tools run on your own provider key (People Data Labs or Twilio). Connect it with
ravn keys add pdl(ortwilio) so those calls do not return 403provider_key_required; our own checks need no key. - / 03
Log the request id
Every response has
metadata.request_id. Store it with your record; it is the fastest way for us to trace a question about a specific answer.
Reading the envelope
Every tool returns the same envelope: status, results and metadata. metadata.fair_use.lookups_remaining_5h tells you how much of the window is left, so a long job can slow itself down before it ever sees a 429. A 403 provider_key_required means a people, company or carrier tool needs your own provider key connected.
Errors come back with a non-200 status and {"error": {"code", "message"}}. The client above turns those into a RavnError with the code attached, so your code can catch provider_key_required differently from invalid_input. The full list is in the error reference, and the limits section covers the 1 MB body cap.
Can I run calls in parallel threads?
Yes, but a small pool is enough, and batching with validate_emails usually beats threads. If you do go parallel, watch lookups_remaining_5h in the metadata and back off before the window runs out.
Is there an official Python SDK?
No, and you do not need one. Each tool is one POST with a JSON body, and the response shape is the same for all 25 tools, so the client above covers all of them.
How long can Retry-After be?
A short rate limit asks for seconds. If the 5-hour fair-use window is used up, it can be much longer. That is why the client raises instead of sleeping past max_wait; schedule the job to resume, or see how the window works.
Which errors are safe to retry?
Retry 429 after the wait and 503 with backoff. Do not retry 401, 403, 404, 413 or 422: they will fail the same way until you change the API key, connect a provider key, or fix the tool name or input.
More in integrations
Every page uses the same account, plan and tools. See pricing →