[{"data":1,"prerenderedAt":527},["ShallowReactive",2],{"docs-navigation":3,"docs-\u002Fsdk\u002Fpython":204},[4,11,16,21,29,35,42,48,54,59,64,69,74,79,86,92,98,103,108,113,118,123,127,132,137,141,145,148,152,157,160,163,167,170,174,178,181,185,189,193,197,201],{"path":5,"title":6,"category":7,"order":8,"family":9,"subfamily":9,"status":10,"endpointId":9},"\u002Fagents","Agent brief","mcp",40,null,"new",{"path":12,"title":13,"category":14,"order":15,"family":9,"subfamily":9,"status":9,"endpointId":9},"\u002Fapi-reference","API reference","introduction",30,{"path":17,"title":18,"category":14,"order":19,"family":9,"subfamily":9,"status":20,"endpointId":9},"\u002Fauthentication","Authentication",20,"updated",{"path":22,"title":23,"category":24,"order":25,"family":26,"subfamily":27,"status":20,"endpointId":28},"\u002Fendpoints\u002Fcheck-company","Company profile status","endpoints",220,"Fetch","Company","check-company",{"path":30,"title":31,"category":24,"order":32,"family":26,"subfamily":33,"status":20,"endpointId":34},"\u002Fendpoints\u002Fcheck-person","Person profile status",120,"Person","check-person",{"path":36,"title":37,"category":24,"order":38,"family":39,"subfamily":40,"status":9,"endpointId":41},"\u002Fendpoints\u002Fcontact-email","Find Email",600,"Contact","Email","contact-email",{"path":43,"title":44,"category":24,"order":45,"family":39,"subfamily":46,"status":9,"endpointId":47},"\u002Fendpoints\u002Fcontact-phone","Find Phone",610,"Phone","contact-phone",{"path":49,"title":50,"category":24,"order":51,"family":52,"subfamily":27,"status":20,"endpointId":53},"\u002Fendpoints\u002Fenrich-company","Enrich company",90,"Enrichment","enrich-company",{"path":55,"title":56,"category":24,"order":57,"family":52,"subfamily":33,"status":20,"endpointId":58},"\u002Fendpoints\u002Fenrich-person","Enrich profile",80,"enrich-person",{"path":60,"title":61,"category":24,"order":62,"family":26,"subfamily":27,"status":20,"endpointId":63},"\u002Fendpoints\u002Ffetch-company","Company profile",200,"fetch-company",{"path":65,"title":66,"category":24,"order":67,"family":26,"subfamily":27,"status":20,"endpointId":68},"\u002Fendpoints\u002Ffetch-company-live","Company profile live",210,"fetch-company-live",{"path":70,"title":71,"category":24,"order":72,"family":26,"subfamily":33,"status":20,"endpointId":73},"\u002Fendpoints\u002Ffetch-profile","Person profile",100,"fetch-profile",{"path":75,"title":76,"category":24,"order":77,"family":26,"subfamily":33,"status":20,"endpointId":78},"\u002Fendpoints\u002Ffetch-profile-live","Person profile live",110,"fetch-profile-live",{"path":80,"title":81,"category":24,"order":82,"family":83,"subfamily":84,"status":20,"endpointId":85},"\u002Fendpoints\u002Fsearch-companies","Company search",94,"Search","Companies","search-companies",{"path":87,"title":88,"category":24,"order":89,"family":90,"subfamily":9,"status":20,"endpointId":91},"\u002Fendpoints\u002Fsearch-companies-available-fields","Company search available fields",720,"Utility","search-companies-available-fields",{"path":93,"title":94,"category":24,"order":95,"family":83,"subfamily":96,"status":10,"endpointId":97},"\u002Fendpoints\u002Fsearch-people","People search",92,"People","search-people",{"path":99,"title":100,"category":24,"order":101,"family":90,"subfamily":9,"status":10,"endpointId":102},"\u002Fendpoints\u002Fsearch-people-available-fields","People search available fields",710,"search-people-available-fields",{"path":104,"title":105,"category":24,"order":106,"family":83,"subfamily":96,"status":10,"endpointId":107},"\u002Fendpoints\u002Fsearch-people-count","People search count",93,"search-people-count",{"path":109,"title":110,"category":24,"order":111,"family":83,"subfamily":96,"status":10,"endpointId":112},"\u002Fendpoints\u002Fsearch-people-lookalike","People lookalike",95,"search-people-lookalike",{"path":114,"title":115,"category":24,"order":116,"family":83,"subfamily":96,"status":10,"endpointId":117},"\u002Fendpoints\u002Fsearch-people-prompt","Agentic Search",96,"search-people-prompt",{"path":119,"title":120,"category":24,"order":121,"family":90,"subfamily":9,"status":20,"endpointId":122},"\u002Fendpoints\u002Fusage","Usage",700,"usage",{"path":124,"title":125,"category":14,"order":126,"family":9,"subfamily":9,"status":9,"endpointId":9},"\u002Fgetting-started","Getting started",10,{"path":128,"title":129,"category":130,"order":131,"family":9,"subfamily":9,"status":10,"endpointId":9},"\u002Fguides\u002Fagent-skills","Agent skills","cli",70,{"path":133,"title":134,"category":135,"order":136,"family":9,"subfamily":9,"status":20,"endpointId":9},"\u002Fguides\u002Fapi-key-management","API key management","guides",12,{"path":138,"title":139,"category":130,"order":140,"family":9,"subfamily":9,"status":10,"endpointId":9},"\u002Fguides\u002Fcli","Command line interface (CLI)",60,{"path":142,"title":143,"category":135,"order":144,"family":9,"subfamily":9,"status":10,"endpointId":9},"\u002Fguides\u002Fcurrent-position-resolution","Current position and work history ordering",35,{"path":146,"title":147,"category":135,"order":8,"family":9,"subfamily":9,"status":20,"endpointId":9},"\u002Fguides\u002Fdata-freshness","Data freshness",{"path":149,"title":150,"category":135,"order":151,"family":9,"subfamily":9,"status":10,"endpointId":9},"\u002Fguides\u002Fdata-quality","Data quality",18,{"path":153,"title":154,"category":155,"order":156,"family":9,"subfamily":9,"status":10,"endpointId":9},"\u002Fguides\u002Fendpoint-transition-october-2026","Endpoint transition on October 13, 2026","migrations",1,{"path":158,"title":159,"category":135,"order":19,"family":9,"subfamily":9,"status":20,"endpointId":9},"\u002Fguides\u002Ferrors-retries","Error handling & retries",{"path":161,"title":162,"category":135,"order":126,"family":9,"subfamily":9,"status":10,"endpointId":9},"\u002Fguides\u002Ffirst-api-call","Your first API call",{"path":164,"title":165,"category":135,"order":166,"family":9,"subfamily":9,"status":10,"endpointId":9},"\u002Fguides\u002Findustries","Industry values",50,{"path":168,"title":169,"category":7,"order":166,"family":9,"subfamily":9,"status":20,"endpointId":9},"\u002Fguides\u002Fmcp","Connect AI agents (MCP)",{"path":171,"title":172,"category":155,"order":173,"family":9,"subfamily":9,"status":10,"endpointId":9},"\u002Fguides\u002Fmigrating-search-persons-to-people","Migrating people search from persons to people",51,{"path":175,"title":176,"category":135,"order":177,"family":9,"subfamily":9,"status":10,"endpointId":9},"\u002Fguides\u002Fmissing-and-empty-fields","Missing and empty profile fields",42,{"path":179,"title":180,"category":135,"order":15,"family":9,"subfamily":9,"status":20,"endpointId":9},"\u002Fguides\u002Fpolling","Polling, zero setup",{"path":182,"title":183,"category":135,"order":184,"family":9,"subfamily":9,"status":20,"endpointId":9},"\u002Fguides\u002Fproduction-checklist","Production checklist",45,{"path":186,"title":187,"category":135,"order":188,"family":9,"subfamily":9,"status":20,"endpointId":9},"\u002Fguides\u002Frate-limits-credits","Rate limits & credits",25,{"path":190,"title":191,"category":135,"order":192,"family":9,"subfamily":9,"status":20,"endpointId":9},"\u002Fguides\u002Fwebhooks","Webhooks",31,{"path":194,"title":195,"category":135,"order":196,"family":9,"subfamily":9,"status":20,"endpointId":9},"\u002Fguides\u002Fwhich-endpoint-should-i-use","Which endpoint should I use?",15,{"path":198,"title":199,"category":200,"order":126,"family":9,"subfamily":9,"status":10,"endpointId":9},"\u002Fsdk\u002Fjavascript","JavaScript and TypeScript","sdk",{"path":202,"title":203,"category":200,"order":19,"family":9,"subfamily":9,"status":10,"endpointId":9},"\u002Fsdk\u002Fpython","Python",{"id":205,"title":203,"audience":9,"badgeColor":9,"badgeLabel":9,"body":206,"category":200,"description":518,"endpointId":9,"extension":519,"family":9,"lastVerified":520,"links":9,"meta":521,"navigation":522,"order":19,"path":202,"relatedArticles":523,"requiresFlag":9,"seo":524,"status":10,"stem":525,"subfamily":9,"__hash__":526},"docs\u002Fsdk\u002Fpython.md",{"type":207,"value":208,"toc":503},"minimark",[209,213,230,240,244,303,317,321,329,343,347,363,372,379,383,395,406,418,426,435,439,444,455,459,479,487],[210,211,212],"h2",{"id":14},"Introduction",[214,215,216,217,224,225,229],"p",{},"There is no Reverse Contact package to install. The API is JSON over HTTPS, so ",[218,219,223],"a",{"href":220,"rel":221},"https:\u002F\u002Frequests.readthedocs.io",[222],"nofollow","requests"," and the helper below are all a call needs. For typed request and response objects, generate Pydantic models from the OpenAPI spec this site publishes at ",[226,227,228],"code",{},"https:\u002F\u002Fdocs.reversecontact.com\u002Fopenapi.json",", which is updated with each docs release.",[214,231,232,233,236,237,239],{},"Keep the API key out of your code, in an environment variable such as ",[226,234,235],{},"RC_API_KEY",": see ",[218,238,18],{"href":17},".",[210,241,243],{"id":242},"a-small-client","A small client",[214,245,246,247,249,250,253,254,257,258,261,262,261,265,268,269,272,273,268,275,277,278,280,281,284,285,261,288,261,291,294,295,298,299,302],{},"The helper sends one request and follows the retry rule of ",[218,248,159],{"href":158},": on a ",[226,251,252],{},"429",", wait the number of seconds in ",[226,255,256],{},"error.details.retryAfter",", then retry; on a ",[226,259,260],{},"500",", ",[226,263,264],{},"502",[226,266,267],{},"503"," or ",[226,270,271],{},"504",", retry with exponential backoff, a few times at most. It reads the status before the body, because a ",[226,274,264],{},[226,276,267],{}," from the network edge may not be JSON. A ",[226,279,252],{}," with the code ",[226,282,283],{},"DAILY_LIMIT_EXCEEDED"," is the daily limit: it is not retried, since it does not clear within minutes. The helper returns the HTTP status and the response envelope (",[226,286,287],{},"success",[226,289,290],{},"data",[226,292,293],{},"error",", and ",[226,296,297],{},"quotas"," and ",[226,300,301],{},"metadata"," when the response carries them).",[304,305,307],"code-group",{"title":306},"rc_client.py",[308,309,315],"pre",{"className":310,"code":312,"filename":203,"language":313,"meta":314},[311],"language-python","import os\nimport time\n\nimport requests\n\nAPI = \"https:\u002F\u002Fapi.reversecontact.com\u002Fv2\"\nBACKOFF = {500, 502, 503, 504}\nMAX_ATTEMPTS = 5\n\nsession = requests.Session()\nsession.headers[\"Authorization\"] = f\"Bearer {os.environ['RC_API_KEY']}\"\n\n\ndef rc(path, body=None):\n    \"\"\"One call to the API. 429: waits error.details.retryAfter. 500, 502, 503, 504: backs off.\"\"\"\n    for attempt in range(1, MAX_ATTEMPTS + 1):\n        if body is None:\n            response = session.get(f\"{API}{path}\", timeout=30)\n        else:\n            response = session.post(f\"{API}{path}\", json=body, timeout=30)\n\n        # The status first: a 502 or 503 from the network edge may not be JSON.\n        if response.status_code in BACKOFF and attempt \u003C MAX_ATTEMPTS:\n            time.sleep(min(2**attempt, 15))\n            continue\n        try:\n            payload = response.json()\n        except ValueError:\n            payload = {\"success\": False, \"error\": {\"code\": f\"HTTP_{response.status_code}\", \"message\": \"The response is not JSON\"}}\n\n        error = payload.get(\"error\") or {}\n        if response.status_code == 429 and error.get(\"code\") != \"DAILY_LIMIT_EXCEEDED\" and attempt \u003C MAX_ATTEMPTS:\n            time.sleep((error.get(\"details\") or {}).get(\"retryAfter\", 60))\n            continue\n        return response.status_code, payload\n","python","",[226,316,312],{"__ignoreMap":314},[210,318,320],{"id":319},"enrich-a-person","Enrich a person",[304,322,323],{"title":320},[308,324,327],{"className":325,"code":326,"filename":203,"language":313,"meta":314},[311],"from rc_client import rc\n\n# 2 credits when a person is found, a 404 is free.\nstatus, result = rc(\"\u002Fenrich\u002Fpersons\", {\"email\": \"jane.doe@acme.com\"})\n\nif result[\"success\"]:\n    person = result[\"data\"][\"person\"]\n    print(person[\"firstName\"], person[\"lastName\"], person[\"currentPositionTitle\"])\n    print(\"Credits used:\", result[\"quotas\"][\"creditsConsumed\"])\nelif status == 404:\n    print(\"No confident match: nothing was charged\")\nelse:\n    print(status, result[\"error\"][\"code\"], result[\"error\"][\"message\"])\n",[226,328,326],{"__ignoreMap":314},[214,330,331,332,335,336,338,339,342],{},"On Enrich, a ",[226,333,334],{},"404"," is not an error to retry: it means no profile matched with confidence, and it costs nothing. What a ",[226,337,334],{}," means depends on the endpoint: on a cached fetch, a profile that is not in the cache; on ",[226,340,341],{},"\u002Fv2\u002Fwebhooks",", an unknown job. Every error other than the retried ones needs a change to the request or the account before it can succeed.",[210,344,346],{"id":345},"async-jobs","Async jobs",[214,348,349,350,298,352,354,355,358,359,362],{},"The live fetch endpoints, ",[218,351,37],{"href":36},[218,353,44],{"href":43}," answer with a job. Poll it until it succeeds or fails: polling is free and does not count toward your rate limit. Give the loop a maximum duration, and stop on a poll that answers an error: ",[226,356,357],{},"404 WEBHOOK_EVENT_NOT_FOUND"," for an unknown job, ",[226,360,361],{},"410 WEBHOOK_EVENT_EXPIRED"," for a result older than 72 hours.",[304,364,366],{"title":365},"Live fetch and polling",[308,367,370],{"className":368,"code":369,"filename":203,"language":313,"meta":314},[311],"import time\n\nfrom rc_client import rc\n\nstatus, job = rc(\"\u002Ffetch\u002Fpersons\u002Flive\", {\"url\": \"https:\u002F\u002Fsocial.com\u002Fin\u002Fjanedoe\"})\nif not job[\"success\"]:\n    raise RuntimeError(f\"{status} {job['error']['code']}: {job['error']['message']}\")\nwebhook_id = job[\"data\"][\"webhookId\"]\n\n# Results usually arrive within seconds, but can take tens of minutes.\ndeadline = time.monotonic() + 30 * 60\n\nwhile True:\n    if time.monotonic() > deadline:\n        raise TimeoutError(f\"Job {webhook_id} still running: poll it again later\")\n    time.sleep(5)\n    status, poll = rc(f\"\u002Fwebhooks\u002F{webhook_id}\")\n    # 404 and 410 mean there is nothing left to wait for.\n    if not poll[\"success\"]:\n        raise RuntimeError(f\"{status} {poll['error']['code']}: {poll['error']['message']}\")\n    if poll[\"data\"][\"status\"] == \"succeeded\":\n        print(poll[\"data\"][\"result\"])\n        break\n    if poll[\"data\"][\"status\"] == \"errored\":\n        raise RuntimeError(poll[\"data\"][\"errorCode\"])\n",[226,371,369],{"__ignoreMap":314},[214,373,374,375,378],{},"Every 5 to 10 seconds suits a background job. See ",[218,376,377],{"href":179},"Polling"," for the intervals that fit other cases, and for how long a result stays available.",[210,380,382],{"id":381},"typed-models-from-the-openapi-spec","Typed models from the OpenAPI spec",[214,384,385,390,391,394],{},[218,386,389],{"href":387,"rel":388},"https:\u002F\u002Fgithub.com\u002Fkoxudaxi\u002Fdatamodel-code-generator",[222],"datamodel-code-generator"," turns the spec into Pydantic models, one per request body and response object. The spec declares email fields, so the models need the ",[226,392,393],{},"email"," extra of Pydantic.",[214,396,397,398,401,402,405],{},"The spec closes its objects (",[226,399,400],{},"additionalProperties: false","), so by default every generated model refuses fields it does not know, and a field the API adds later would make every response fail validation. ",[226,403,404],{},"--extra-fields ignore"," keeps the models working when the API grows.",[304,407,409],{"title":408},"Generate the models",[308,410,416],{"className":411,"code":413,"filename":414,"language":415,"meta":314},[412],"language-bash","pip install \"datamodel-code-generator[http]\" \"pydantic[email]\"\ndatamodel-codegen --url https:\u002F\u002Fdocs.reversecontact.com\u002Fopenapi.json \\\n  --input-file-type openapi \\\n  --output-model-type pydantic_v2.BaseModel \\\n  --extra-fields ignore \\\n  --output reversecontact_models.py\n","pip","bash",[226,417,413],{"__ignoreMap":314},[214,419,420,421,425],{},"Run it again whenever the ",[218,422,424],{"href":423},"\u002Fchangelog","changelog"," announces a change to an endpoint you call. The models validate what you send and what you read:",[304,427,429],{"title":428},"Typed request and response",[308,430,433],{"className":431,"code":432,"filename":203,"language":313,"meta":314},[311],"from rc_client import rc\nfrom reversecontact_models import EnrichPersonBody, EnrichPersonData\n\nbody = EnrichPersonBody(email=\"jane.doe@acme.com\")\nstatus, result = rc(\"\u002Fenrich\u002Fpersons\", body.model_dump(exclude_none=True))\n\nif result[\"success\"]:\n    data = EnrichPersonData.model_validate(result[\"data\"])\n    print(data.person.firstName, data.person.currentPositionTitle, data.isFullProfile)\n",[226,434,432],{"__ignoreMap":314},[210,436,438],{"id":437},"faq","FAQ",[440,441,443],"h3",{"id":442},"is-there-an-official-reverse-contact-python-package","Is there an official Reverse Contact Python package?",[214,445,446,447,449,450,452,453,239],{},"No. The Reverse Contact API is JSON over HTTPS, so ",[226,448,223],{}," and a small helper are enough, and ",[226,451,389],{}," builds typed Pydantic models from ",[226,454,228],{},[440,456,458],{"id":457},"how-should-a-python-script-handle-rate-limits-on-the-reverse-contact-api","How should a Python script handle rate limits on the Reverse Contact API?",[214,460,461,462,253,464,466,467,469,470,261,472,261,474,298,476,478],{},"On a ",[226,463,252],{},[226,465,256],{}," and send the request again, except for ",[226,468,283],{},", the daily limit. Retry ",[226,471,260],{},[226,473,264],{},[226,475,267],{},[226,477,271],{}," with exponential backoff, a few times at most, and do not retry other errors without changing the request.",[440,480,482,483,486],{"id":481},"why-do-the-generated-reverse-contact-models-need-pydanticemail","Why do the generated Reverse Contact models need ",[226,484,485],{},"pydantic[email]","?",[214,488,489,490,492,493,496,497,499,500,502],{},"The Reverse Contact OpenAPI spec declares email fields, such as ",[226,491,393],{}," on ",[226,494,495],{},"EnrichPersonBody",", which Pydantic validates with its optional ",[226,498,393],{}," extra. Install ",[226,501,485],{}," alongside the models.",{"title":314,"searchDepth":504,"depth":504,"links":505},3,[506,508,509,510,511,512],{"id":14,"depth":507,"text":212},2,{"id":242,"depth":507,"text":243},{"id":319,"depth":507,"text":320},{"id":345,"depth":507,"text":346},{"id":381,"depth":507,"text":382},{"id":437,"depth":507,"text":438,"children":513},[514,515,516],{"id":442,"depth":504,"text":443},{"id":457,"depth":504,"text":458},{"id":481,"depth":504,"text":517},"Why do the generated Reverse Contact models need pydantic[email]?","Call the Reverse Contact API from Python with a small requests helper, and generate typed Pydantic models from the OpenAPI spec.","md","2026-10-15",{},true,"\u002Fdocs\u002Fsdk\u002Fjavascript,\u002Fdocs\u002Fauthentication,\u002Fdocs\u002Fguides\u002Ferrors-retries,\u002Fdocs\u002Fguides\u002Fpolling",{"title":203,"description":518},"sdk\u002Fpython","wsAj_91iZVtfPKRySjKXOqr8nSKG3_gATDKq0EuQbFI",1792078622095]