Documentation

esc
    SDKs / Python

    Python SDK

    Install pantech from PyPI, create a client, make a machine and wait for it, and handle errors, pages and retries.

    Python
    import pantech
    
    client = pantech.Pantech()
    print(client.me.get()["organization_id"])

    pantech is the official Python library. It needs Python 3.9 or later, and its only dependency is httpx.

    Install

    Terminal
    $ pip install pantech

    Authenticate

    Make an API key and put it in PANTECH_API_KEY. The client reads it.

    Terminal
    $ export PANTECH_API_KEY=PAN_…

    Or pass it, with an optional base URL (also read from PANTECH_API_URL):

    Python
    import os
    
    import pantech
    
    client = pantech.Pantech(
        api_key=os.environ["MY_PANTECH_KEY"],
        base_url="https://api.pantechdynamics.com/public/v1",
        max_retries=5,  # default 3
    )

    The other arguments are timeout (an httpx.Timeout, default 60 seconds), default_headers, user_agent and http_client (your own httpx.Client). Use the client as a context manager, or call client.close(), to close its connections.

    Create a machine and wait for it

    create.py
    import pantech
    
    client = pantech.Pantech()
    
    order = client.instances.create(
        name="web-01",
        plan_slug="starter",
        image_slug="ubuntu-24-04",
        ssh_key_id="sshk_…",
    )
    
    # Creating a machine places an order: payment, then provisioning.
    # wait_for_order returns the order once it is provisioned.
    done = client.instances.wait_for_order(order["order_id"])
    instance = client.instances.get(done["instance_id"])
    print(instance["name"], instance["public_ipv4"])

    wait_for_order raises pantech.OrderFailedError if the payment or the order fails, and pantech.WaitTimeoutError after 15 minutes (pass timeout= in seconds to change it). Databases have the same client.databases.wait_for_order.

    Read real slugs from the catalogue first: client.catalog.list_all_plans(), list_all_images(), list_all_regions() and list_all_disk_offerings().

    Resources

    Each part of the API is an attribute of the client:

    client.instances, client.networks, client.public_ips, client.security_groups, client.load_balancers, client.volumes, client.snapshots, client.databases, client.kubernetes, client.ssh_keys, client.webhooks, client.operations, client.catalog, client.billing, client.usage and client.me.

    Families nest where the API does, for example client.networks.subnets, client.networks.firewall_rules, client.public_ips.port_forwarding_rules and client.kubernetes.node_pools. Responses are plain dictionaries, typed with TypedDicts from pantech.types.

    Async

    pantech.AsyncPantech has the same methods, to await:

    Python
    import asyncio
    import pantech
    
    async def main():
        async with pantech.AsyncPantech() as client:
            me = await client.me.get()
            print(me["organization_id"])
    
    asyncio.run(main())

    In the async client, list_all returns an async iterator: async for instance in client.instances.list_all():.

    Errors

    Every API error is a pantech.APIError, with status, code, title, detail, request_id and field errors. Common ones have their own class:

    ClassWhen
    BadRequestError400
    AuthenticationError401: the key is wrong, expired or revoked
    PaymentRequiredError402: not enough credit for this order
    PermissionDeniedError403: the key's scope or its creator's role does not allow it, or AI apps are turned off
    NotFoundError404
    ConflictError409
    ValidationError422: see errors for each bad field
    RateLimitError429, after the retries ran out; retry_after is the wait asked for
    InternalServerError5xx, after the retries ran out

    Errors that are not an API answer: APIConnectionError (the request never got an answer) and its subclass APITimeoutError, OperationFailedError and OrderFailedError (a wait ended in failure), WaitTimeoutError and WebhookVerificationError. All of them, APIError included, are a pantech.PantechError.

    Python
    try:
        client.instances.get("vm_…")
    except pantech.NotFoundError:
        print("no such machine")
    except pantech.APIError as e:
        print(e.code, e.detail, e.request_id)

    Act on code, never the wording. Every code.

    Pages

    list() returns one page: a dictionary with data and next_cursor. list_all() loops over every item and follows next_cursor for you:

    Python
    for instance in client.instances.list_all():
        print(instance["name"])

    Operations

    Most changes return an operation. Wait for it before you rely on the result:

    Python
    accepted = client.instances.stop("vm_…")
    op = client.operations.wait(accepted["operation_id"])
    print(op["status"])  # "succeeded"

    wait polls GET /operations/{id} every 2 seconds. It raises pantech.OperationFailedError (with code and reason) if the operation fails, unless you pass raise_on_failure=False, and pantech.WaitTimeoutError after 10 minutes (timeout= in seconds, None to wait for ever).

    Idempotency and retries

    Every POST, PUT, PATCH and DELETE gets an Idempotency-Key automatically. The same key is sent on every retry, so the change happens once. To choose it yourself, pass idempotency_key= to any write method.

    The client retries network errors, timeouts, 429 (after Retry-After), 500, 502, 503 and 504, up to 3 times, with a growing, random wait. Calls the API cannot replay safely, such as adding an SSH key, are sent once, and only a 429 is retried. The list.

    Calling any endpoint

    client.request calls a path directly, with the same auth, retries and errors, and returns the parsed body:

    Python
    usage = client.request("GET", "/usage")

    It also takes query=, body=, idempotency_key=, extra_headers=, timeout= and max_retries=. Every typed method takes extra_headers=, extra_query= and timeout= too.

    Webhooks

    pantech.verify_webhook(secret, header, body) checks a delivery's signature. It returns None, and raises pantech.WebhookVerificationError if the signature does not verify. Verify webhooks with the SDKs.

    Was this page helpful?

    Updated