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:

bash
pip install render

The 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.

python
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")
The sandbox environment is completely free and unlimited. Use 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.

file_ct600.py
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 ID

The returned Filing object:

python
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',
)
ParameterTypeDescription
companyrequired
dictCompany details. UTR, name, and type are required.
periodrequired
dictAccounting period. start and end as ISO 8601 date strings.
financialsrequired
dictTax computation figures. All monetary values in pence (integers).
accountsrequired
dictAccounts standard (FRS102 or FRS105), or ixbrl with base64-encoded iXBRL.
idempotency_key
strOptional. Supply the same key to safely retry without creating a duplicate filing.

Retrieve a filing

python
filing = client.filings.retrieve("fil_01j9xkz7...")

print(filing.status)   # "accepted" once HMRC processes it

HMRC processes submissions asynchronously. Poll retrieve() until status is accepted or failed, or use webhooks to receive a push notification.

To list recent filings:

python
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.

python
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 dicts

Amend a filing

Corrections to an accepted filing are submitted as amendments. Pass the ID of the original filing and only the changed fields.

python
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"
Amendments are billed at the same rate as original filings (£2 per live amendment on pay as you go). Sandbox amendments are always free.

Error handling

All errors are subclasses of RenderError and carry code, message, field, doc_url, and status attributes.

error_handling.py
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}")
Always catch 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

ParameterTypeDescription
api_keyrequired
strYour Render API key. sk_sandbox_ for testing, sk_live_ for production.
base_url
strOverride the API base URL. Defaults to https://api.render.my (live) or http://localhost:8000 (sandbox).
timeout
floatRequest 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():

batch.py
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"]))