{"schema_version":"1.0","publisher":"Primitive SDKs","canonical_url":"https://test.abhinandan.one/python-sdk-quickstart","markdown_url":"https://test.abhinandan.one/python-sdk-quickstart.md","article":{"id":"6ecd3ac2-a0bb-411b-a3af-9463df80af3f","article_slug":"python-sdk-quickstart","parent_article_slug":null,"parent_article_title":null,"kind":"quickstart","published_at":"2026-08-11T18:54:59.021182+00:00","keywords":["pip install primitivedotdev","primitive.client","PrimitiveClient","PRIMITIVE_API_KEY","api_base_url_1 api_base_url_2","with_options timeout"],"meta_description":"Install the primitivedotdev package, set PRIMITIVE_API_KEY, and send your first outbound email with client.send in under five minutes.","og_image_url":null,"source_file_paths":["sdk-python/README.md"],"recording_id":null,"replayable":false,"task_name":"Install and Configure the Python SDK","category":"Python SDK","summary":null,"description":"Install primitivedotdev, create a PrimitiveClient with your API key, and send your first email in a few lines of Python.","content_kind":"repo_page","content_markdown":"Install `primitivedotdev`, create a client with your API key, and send your first outbound email. Requires Python `>=3.10`.\n\n<Note>\n\nThis page covers the Python SDK. For the Node.js SDK, see [Node.js SDK Quickstart](node-sdk-quickstart); for Go, see [Go SDK Quickstart](go-sdk-quickstart). All three implement the same [inbound/outbound email model](email-model), so pick by language, not by capability.\n\n</Note>\n\n<Steps>\n\n<Step title=\"Install the package\">\n\n```bash\npip install primitivedotdev\n```\n\nThe import name is `primitive` (distinct from the PyPI package name `primitivedotdev`).\n\n</Step>\n\n<Step title=\"Set your API key\">\n\nGet a key from your [dashboard](https://primitive.dev) and export it:\n\n```bash\nexport PRIMITIVE_API_KEY=prim_test\n```\n\nRead it from the environment when constructing the client, rather than hardcoding it:\n\n```python\nimport os\nimport primitive\n\nclient = primitive.client(api_key=os.environ[\"PRIMITIVE_API_KEY\"])\n```\n\n</Step>\n\n<Step title=\"Send your first email\">\n\n```python\nimport os\nimport primitive\n\nclient = primitive.client(api_key=os.environ[\"PRIMITIVE_API_KEY\"])\n\nresult = client.send(\n    from_email=\"Support <support@example.com>\",\n    to=\"alice@example.com\",\n    subject=\"Hello\",\n    body_text=\"Hi there\",\n)\n\nprint(result.id, result.status, result.queue_id, result.delivery_status)\n```\n\n</Step>\n\n<Step title=\"Verify the result\">\n\nA successful call returns a `SendResult` dataclass. By default `send` returns as soon as Primitive accepts the message for delivery, so `result.delivery_status` is `None` at this point. The printed line looks like this (ids will differ):\n\n```text\n<send-id> submitted_to_agent <queue-id> None\n```\n\nTo confirm the actual SMTP outcome (`delivered`, `bounced`, `deferred`, or `wait_timeout`) instead of just acceptance, pass `wait=True`; see [Sending Email](python-send-email) for the full wait-mode contract and delivery-status meanings.\n\n</Step>\n\n</Steps>\n\n## Configuring the client\n\n`primitive.client(...)` returns a `PrimitiveClient`. The constructor accepts:\n\n| Parameter | Purpose |\n| --- | --- |\n| `api_key` | Required. Your Primitive API key. |\n| `api_base_url_1` | Primary API host. Defaults to `DEFAULT_API_BASE_URL_1`. |\n| `api_base_url_2` | Attachment-capable host used for `send`/`reply`. Defaults to `DEFAULT_API_BASE_URL_2`. |\n| `**client_kwargs` | Forwarded to the underlying `AuthenticatedClient`, including `timeout` in seconds. |\n\n```python\nimport primitive\n\nclient = primitive.client(\n    api_key=\"prim_test\",\n    timeout=60.0,  # seconds; raise this when you plan to pass wait=True\n)\n```\n\n<Note>\n\nInternally the SDK is a dual-host client: most operations hit `api_base_url_1`, while `send` and `reply` route to `api_base_url_2`, which accepts larger request bodies for attachments. The split is transparent; you never choose the host yourself, and both defaults point at production.\n\n</Note>\n\n<Warning>\n\n`PrimitiveClient` no longer accepts a bare `base_url` keyword. Passing it raises a `TypeError` explaining the rename to `api_base_url_1` / `api_base_url_2`.\n\n</Warning>\n\n### Per-call and default timeouts\n\nEvery `send`, `reply`, and `forward` call (and their `a*` async variants) accepts per-call `timeout`, `extra_headers`, and `idempotency_key` keyword arguments. Use `client.with_options(...)` to change the client-wide defaults without repeating them on every call:\n\n```python\nfast = client.with_options(timeout=5.0)\nfast.send(\n    from_email=\"support@example.com\",\n    to=\"alice@example.com\",\n    subject=\"Hello\",\n    body_text=\"Hi there\",\n)\n```\n\nPer-call kwargs still win over `with_options` defaults, and `with_options` accepts only `timeout` and `extra_headers`; `idempotency_key` is rejected as a client default. Full details live on [Client and Request Options](python-client-options).\n\n## Next call: receive and reply\n\nSending is half the story. `primitive.receive(...)` turns an inbound webhook into a normalized `ReceivedEmail`:\n\n```python\nimport primitive\n\nclient = primitive.client(api_key=\"prim_test\")\n\n\ndef webhook_handler(body: bytes, headers: dict[str, str]) -> dict[str, object]:\n    email = primitive.receive(\n        body=body,\n        headers=headers,\n        secret=\"whsec_...\",\n    )\n\n    client.reply(email, \"Thank you for your email.\")\n    return {\"ok\": True}\n```\n\nThe normalized email object and the receive/send/reply/forward flow are explained once, for every SDK, on [Inbound and Outbound Email Model](email-model). For the Python-specific mechanics of receiving and parsing, see [Receiving and Parsing Inbound Email](python-receive-email); for sending in depth, see [Sending Email](python-send-email).\n\n<Tip>\n\nIf an AI coding agent is doing the integration, point it at the [Agent Guide](agent-guide) instead: install commands, canonical API shapes, and repo conventions on one page.\n\n</Tip>\n\n## Next steps\n\n<CardGroup cols={2}>\n\n<Card title=\"Client and Request Options\" href=\"python-client-options\">\n\nConfigure timeouts, dual-host base URLs, and per-call overrides with with_options.\n\n</Card>\n\n<Card title=\"Sending Email\" href=\"python-send-email\">\n\nControl idempotency and wait-for-delivery semantics, and interpret delivery status.\n\n</Card>\n\n<Card title=\"Receiving and Parsing Inbound Email\" href=\"python-receive-email\">\n\nTurn a raw inbound webhook into a normalized ReceivedEmail object.\n\n</Card>\n\n<Card title=\"Inbound and Outbound Email Model\" href=\"email-model\">\n\nThe normalized email object and wait-mode delivery statuses shared across every SDK.\n\n</Card>\n\n</CardGroup>","canonical_base_url":"https://test.abhinandan.one","seo_indexing_enabled":true,"last_modified":"2026-08-21T18:22:43.359885+00:00","video_url":null,"voiceover_url":null,"tools_used":[],"demonstrated_by":[],"steps":[],"related_links":[],"intro":null,"prerequisites":[],"verification":[],"troubleshooting":[],"suggest_edit_url":"https://github.com/abhi-browzer/primitive-sdks/edit/main/sdk-python/README.md","raise_issue_url":"https://github.com/abhi-browzer/primitive-sdks/issues/new?title=Docs+feedback%3A+Install+and+Configure+the+Python+SDK&body=Page%3A+https%3A%2F%2Ftest.abhinandan.one%2Fpython-sdk-quickstart","page_feedback_enabled":true,"verified_ref":null,"verified_at":"2026-08-11T18:38:45.205849+00:00"}}