> ## Documentation Index
> Fetch the complete documentation index at: https://docs.stockshift.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Python

> A Python client for the StockShift API. It is generated from the API's OpenAPI document. It has a method for every operation and a typed model for every response.

It needs Python 3.10 or later. It is built on httpx and Pydantic v2, and it ships full type hints that pass mypy and pyright in strict mode.

## Install

```bash theme={null}
pip install stockshift
```

## Quickstart

Create an item, then list stock on hand:

```python theme={null}
import os

from stockshift import StockShift

with StockShift(token=os.environ["STOCKSHIFT_TOKEN"]) as stockshift:
    stockshift.catalog.create_item(
        sku="BOLT-M4", name="M4 hex bolt", type="stocked", make_or_buy="buy"
    )

    for row in stockshift.inventory.iter_stock_on_hand():
        print(row.sku, row.location_code, row.available_to_promise)
```

`AsyncStockShift` has the same methods for asyncio and Trio. Await each call and page with `async for`:

```python theme={null}
from stockshift import AsyncStockShift

async with AsyncStockShift(token=os.environ["STOCKSHIFT_TOKEN"]) as stockshift:
    async for item in stockshift.catalog.iter_items(search="bolt"):
        print(item.sku)
```

## Authentication

Pass an API key or an OAuth access token as `token`. Create an API key in the app under Settings > Access > API keys. The key acts as the member who made it, and a call needs both the member's permission and one of the key's scopes.

For an OAuth token that expires, pass a function. The client calls it before each request. `AsyncStockShift` also takes a coroutine function:

```python theme={null}
stockshift = StockShift(token=lambda: tokens.current())
```

Other arguments:

| Argument | Default | Meaning |
| - | - | - |
| `base_url` | `https://app.stockshift.io/api/v1` | Where the API is. |
| `max_retries` | `2` | Retries for a call that can be retried. |
| `timeout` | `60` | Seconds to wait for each attempt. |
| `max_retry_after` | `60` | The longest `Retry-After`, in seconds, worth waiting for. |
| `headers` | none | Headers sent on every call. |
| `http_client` | a new one | An `httpx.Client` or `httpx.AsyncClient` to send with. |

`with_options()` returns a copy with other settings that shares the connections:

```python theme={null}
stockshift.with_options(timeout=120, max_retries=5).catalog.list_units_of_measure()
```

## Making calls

Each area of the API is an attribute, such as `catalog` or `inventory`, and each operation is a method on it. Path parameters come first. Everything else is a keyword argument:

```python theme={null}
item = stockshift.catalog.get_item("01J8QZ4M...")
print(item.sku)

page = stockshift.catalog.list_items(search="bolt", status="active")
```

Every method's docstring names its path, such as `GET /catalog/items/{item}`, and copies the API reference.

A call returns a Pydantic model from `stockshift.models`, or a list of them. An empty answer returns `None`, a CSV export its text and a file the `httpx.Response`. A field the API adds after this release is kept on the model, and you can read it from `model_extra`.

An optional body field you leave out is not sent. Pass `None` to send `null`, which clears the field.

To call a path this release has no method for, use `request()`. It returns the `httpx.Response`:

```python theme={null}
response = stockshift.request("GET", "/catalog/items/{item}", path_params={"item": item.id})
```

## Pagination

A list that can grow returns one page at a time. Each paged list has a second method that starts with `iter_`. It follows the cursors and yields each record:

```python theme={null}
for item in stockshift.catalog.iter_items(search="bolt"):
    print(item.sku)
```

It fetches the next page only when the records of the last one run out. To page by hand, pass each page's `next_cursor` back as `after` until it is `None`.

## Errors

A failed call raises an `APIError` that holds the RFC 9457 problem details body. Branch on `code`, which is stable. `detail` is written for people and may change.

```python theme={null}
from stockshift import APIError

try:
    stockshift.catalog.create_item(
        sku="BOLT-M4", name="M4 hex bolt", type="stocked", make_or_buy="buy"
    )
except APIError as error:
    if error.code == "validation_failed":
        print(error.errors)  # {'sku': ['Another item already has the SKU BOLT-M4.']}
```

`APIError` also has `status`, `problem`, `headers` and `response`. A call that gets no answer, because the network failed or the attempt timed out, raises an `APIConnectionError`. Both extend `StockShiftError`, which is also raised for an answer this release cannot read.

## Retries and idempotency keys

The client retries a `429` after the time in `Retry-After`. It does this for every method, because the API did not run the call. If `Retry-After` is longer than `max_retry_after`, it raises instead.

A `5xx` or a network failure is retried for `GET`, `PUT` and `DELETE`, with a short backoff. A `POST` or `PATCH` is retried only when you give it an idempotency key, so a retry cannot make the change twice:

```python theme={null}
import uuid

stockshift.integrations.push_stock_now(connection_id, idempotency_key=str(uuid.uuid4()))
```

Use a new key for each change you mean to make. The endpoints that honor the key say so in the API reference.

## Webhooks

Check the `StockShift-Signature` header before you trust a delivery. `verify_webhook()` needs the raw body, exactly as it arrived:

```python theme={null}
from stockshift import WebhookVerificationError, verify_webhook
from stockshift.models import InventoryStockAdjustedEvent


def receive(body: bytes, signature: str | None) -> int:
    try:
        event = verify_webhook(body, signature, os.environ["STOCKSHIFT_WEBHOOK_SECRET"])
    except WebhookVerificationError:
        return 400

    if isinstance(event, InventoryStockAdjustedEvent):
        print(event.data.item_id, event.data.quantity)
    return 204
```

It returns a model for each event type, or an `UnknownWebhookEvent` for a type added after this release. It refuses a delivery more than five minutes old. Pass `tolerance`, in seconds, to change that. Retries of an event keep its `id`, so store it and skip events you have handled.

To test your own receiver, `sign_webhook()` signs a payload the same way StockShift does.

## Versions

The major version moves when the API changes in a way that can break your code, such as a removed field or a new required parameter. New endpoints and fields come in minor versions. `stockshift.API_VERSION` holds the version of the API reference the package was built from.


## Related topics

- [StockShift for developers](https://docs.stockshift.io/index.md)
- [SDKs](https://docs.stockshift.io/sdks/index.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.