Python SDK
The official Python library for the Render API. Supports Python 3.9+ and uses httpx for async-friendly HTTP.
Installation
Install the package from PyPI:
pip install renderThe SDK requires Python 3.9 or later and has a single dependency: httpx.
Initialise the client
Import Render and pass your API key. The client auto-detects sandbox vs live from the key prefix — no extra configuration needed.
from render import Render
# Sandbox (default — free, unlimited, no card needed)
client = Render(api_key="sk_sandbox_your_key_here")
# Live — routes to HMRC production
client = Render(api_key="sk_live_your_key_here")sk_sandbox_ keys for all development and testing. Sandbox submissions are validated identically to live but routed to HMRC's test infrastructure, not production.Create a filing
client.filings.create() validates the payload, builds the CT600 XML and iXBRL documents, signs the GovTalk envelope, and submits to HMRC. It returns when HMRC acknowledges receipt.
from render import Render
client = Render(api_key="sk_sandbox_test_key")
filing = client.filings.create(
company={
"utr": "1234567890",
"name": "Acme Ltd",
"type": "limited-company",
},
period={
"start": "2024-01-01",
"end": "2024-12-31",
},
financials={
"trading_profit": 4800000, # £48,000 in pence
"tax_adjusted_profit": 4800000,
"ct_rate_pct": 25,
"ct_charge": 1200000, # £12,000 in pence
},
accounts={
"standard": "FRS102",
},
)
print(filing.id) # fil_01j...
print(filing.status) # "submitted"
print(filing.correlation_id) # HMRC correlation IDThe returned Filing object:
Filing(
id='fil_01j9xkz7...',
status='submitted',
correlation_id='20251105T143021Z-GB-7b2f9...',
period_start='2024-01-01',
period_end='2024-12-31',
company_utr='1234567890',
schema_version='v1.993',
created_at='2025-11-05T14:30:21Z',
updated_at='2025-11-05T14:30:23Z',
)| Parameter | Type | Description |
|---|---|---|
companyrequired | dict | Company details. UTR, name, and type are required. |
periodrequired | dict | Accounting period. start and end as ISO 8601 date strings. |
financialsrequired | dict | Tax computation figures. All monetary values in pence (integers). |
accountsrequired | dict | Accounts standard (FRS102 or FRS105), or ixbrl with base64-encoded iXBRL. |
idempotency_key | str | Optional. Supply the same key to safely retry without creating a duplicate filing. |
Retrieve a filing
filing = client.filings.retrieve("fil_01j9xkz7...")
print(filing.status) # "accepted" once HMRC processes itHMRC processes submissions asynchronously. Poll retrieve() until status is accepted or failed, or use webhooks to receive a push notification.
To list recent filings:
filings = client.filings.list(limit=20)
for f in filings.data:
print(f.id, f.status)Validate without submitting
client.filings.validate() runs the full validation pipeline — schema, BVR rules, iXBRL consistency — but does not submit to HMRC and does not consume any credits.
result = client.filings.validate(
company={
"utr": "1234567890",
"name": "Acme Ltd",
"type": "limited-company",
},
period={
"start": "2024-01-01",
"end": "2024-12-31",
},
financials={
"trading_profit": 4800000,
"tax_adjusted_profit": 4800000,
"ct_rate_pct": 25,
"ct_charge": 1200000,
},
accounts={"standard": "FRS102"},
)
print(result.valid) # True / False
print(result.errors) # [] or list of error dictsAmend a filing
Corrections to an accepted filing are submitted as amendments. Pass the ID of the original filing and only the changed fields.
amendment = client.filings.amend(
filing_id="fil_01j9xkz7...",
financials={
"trading_profit": 5000000,
"tax_adjusted_profit": 5000000,
"ct_rate_pct": 25,
"ct_charge": 1250000,
},
)
print(amendment.id) # fil_01jb... (new ID)
print(amendment.status) # "submitted"Error handling
All errors are subclasses of RenderError and carry code, message, field, doc_url, and status attributes.
from render import Render
from render.errors import (
RenderError,
AuthenticationError,
ValidationError,
NotFoundError,
RateLimitError,
)
client = Render(api_key="sk_sandbox_test_key")
try:
filing = client.filings.create(...)
except AuthenticationError:
# Invalid or missing API key
print("Check your API key")
except ValidationError as e:
# Schema or HMRC validation failure
print(f"Validation failed: {e.code} — {e.message}")
print(f"Field: {e.field}")
print(f"Docs: {e.doc_url}")
except RateLimitError:
# Back off and retry
time.sleep(60)
except RenderError as e:
# Catch-all for any Render error
print(f"Error {e.status}: {e.message}")ValidationError explicitly. HMRC validation errors (error codes 1600–1699) are surfaced here with the original HMRC message. Do not retry these without fixing the payload.Configuration reference
| Parameter | Type | Description |
|---|---|---|
api_keyrequired | str | Your Render API key. sk_sandbox_ for testing, sk_live_ for production. |
base_url | str | Override the API base URL. Defaults to https://api.render.my (live) or http://localhost:8000 (sandbox). |
timeout | float | Request timeout in seconds. Default: 30. |
Async / concurrent usage
The SDK is built on httpx which supports async natively. You can batch filings with asyncio.gather():
import asyncio
import httpx
from render import Render
# The SDK uses httpx — you can pass your own async client
async def file_returns(utrs: list[str]):
client = Render(api_key="sk_sandbox_test_key")
tasks = [
client.filings.create(
company={"utr": utr, "name": f"Company {utr}", "type": "limited-company"},
period={"start": "2024-01-01", "end": "2024-12-31"},
financials={"trading_profit": 0, "tax_adjusted_profit": 0,
"ct_rate_pct": 25, "ct_charge": 0},
accounts={"standard": "FRS105"},
)
for utr in utrs
]
results = await asyncio.gather(*tasks, return_exceptions=True)
return results
asyncio.run(file_returns(["1234567890", "0987654321"]))