Skip to main content

Documentation Index

Fetch the complete documentation index at: https://www.octogen.ai/docs/llms.txt

Use this file to discover all available pages before exploring further.

The Octogen Python SDK gives you an async, type-safe client for the Octogen commerce API. Install it with pip, point it at your OCTO_API_KEY, and you can list catalogs, search products by keyword and facet, and look up any product by URL — all backed by Pydantic models so your IDE can autocomplete every field.

Installation

1

Install the package

Requires Python 3.11 or later.
pip install octogen-ai-sdk
2

Set your API key

The client reads your key from the OCTO_API_KEY environment variable automatically.
export OCTO_API_KEY=octo_live_...
You can also pass api_key= directly to the constructor if you prefer to manage secrets yourself (see Authentication below).

Authentication

OctogenClient resolves your API key in this order:
  1. The api_key constructor argument, if provided.
  2. The OCTO_API_KEY environment variable.
If neither is set, the constructor raises MissingAPIKeyError immediately — before any network request is made. Every request then sends the key as Authorization: Bearer <api-key>.
from octogen_ai_sdk import OctogenClient

# From environment variable (recommended)
client = OctogenClient()

# Explicit key
client = OctogenClient(api_key="octo_live_...")

Client constructor

OctogenClient accepts the following keyword-only arguments:
ParameterTypeDefaultDescription
api_keystr | NoneNone (reads OCTO_API_KEY)Your Platform API key.
base_urlstrhttps://api.octogen.ai/v1Override the API base URL (useful for testing).
timeoutfloat | httpx.Timeout30.0Request timeout in seconds, or a full httpx.Timeout object.
http_clienthttpx.AsyncClient | NoneNone (SDK creates one)Supply your own httpx.AsyncClient to reuse connections or configure proxies.

Using the client as an async context manager

Use async with to ensure the underlying HTTP connection pool is closed when you are done:
import asyncio
from octogen_ai_sdk import OctogenClient

async def main() -> None:
    async with OctogenClient() as client:
        catalogs = await client.list_catalogs()
        print(catalogs)

asyncio.run(main())
If you manage the client’s lifetime yourself, call await client.aclose() when finished.

Methods

list_catalogs

async def list_catalogs() -> list[MerchantCatalogSummary]
Returns all active catalogs granted to your API key’s organization. Use the catalog field from each result as the identifier for search_products.
catalogs = await client.list_catalogs()
for c in catalogs:
    print(c.catalog, c.display_name, c.product_count)
Returns: list[MerchantCatalogSummary] Each MerchantCatalogSummary has:
FieldTypeDescription
catalogstrCatalog identifier — pass this to search_products.
display_namestrHuman-readable catalog name.
product_countintNumber of indexed products.
source_base_urlstr | NoneBase URL of the catalog source.
last_indexed_atdatetime | NoneWhen the catalog was last indexed.

lookup_product

async def lookup_product(url: str) -> MerchantProductUrlLookupResponse
Resolves a canonical product page URL to its full product record, including pricing, images, variants, sizes, and enrichment data. The URL must belong to a catalog granted to your key.
result = await client.lookup_product("https://example.com/products/blue-linen-dress")
print(result.product.title)
print(result.product.current_price)
Returns: MerchantProductUrlLookupResponse
FieldTypeDescription
catalog_keystrCatalog the product belongs to.
catalog_display_namestrHuman-readable catalog name.
productMerchantProductViewFull product record with all detail fields.

search_products

async def search_products(
    *,
    catalog: str,
    q: str | None = None,
    facets: Sequence[Facet | dict] | None = None,
    price_min: float | None = None,
    price_max: float | None = None,
    cursor: str | None = None,
    limit: int = 50,
) -> MerchantProductListPage
Searches products in a single authorized catalog. All parameters except catalog are optional.
ParameterTypeDefaultDescription
catalogstrRequired. Catalog identifier from list_catalogs().
qstr | NoneNoneKeyword search query.
facetsSequence[Facet | dict] | NoneNoneList of facet filters to apply. See Faceted search.
price_minfloat | NoneNoneMinimum price filter (inclusive).
price_maxfloat | NoneNoneMaximum price filter (inclusive).
cursorstr | NoneNonePagination cursor from a previous response’s next_cursor.
limitint50Number of results per page. Must be between 1 and 100.
Returns: MerchantProductListPage
FieldTypeDescription
itemslist[MerchantProductListItem]Products on this page.
next_cursorstr | NonePass as cursor in your next call to get the next page. None means no more results.
Each MerchantProductListItem includes uuid, product_url, title, brand, current_price, original_price, image_url, images, rating, and updated_at.

Complete example

The following example lists your first catalog, searches for women’s linen dresses, and prints each result with its brand and price:
import asyncio
from pprint import pformat

from octogen_ai_sdk import OctogenAPIError, OctogenClient


async def main() -> None:
    try:
        async with OctogenClient() as client:
            catalogs = await client.list_catalogs()
            if not catalogs:
                print("No catalogs are available for this API key.")
                return

            catalog = catalogs[0].catalog
            results = await client.search_products(
                catalog=catalog,
                q="women's linen summer dresses",
                limit=5,
            )

            print(f"Catalog: {catalog}")
            for product in results.items:
                brand = product.brand.name if product.brand else "Unknown brand"
                price = (
                    f"${product.current_price:.2f}"
                    if product.current_price is not None
                    else "Price unavailable"
                )
                title = product.title or "Untitled product"
                print(f"- {title} | {brand} | {price}")
                print(f"  {product.product_url}")
    except OctogenAPIError as exc:
        print(f"Octogen API error: status={exc.status_code}")
        print(pformat(exc.detail))
        raise


if __name__ == "__main__":
    asyncio.run(main())
Use Facet objects to filter by brand, gender, color, category, and other attributes. Pass a list of facets to the facets parameter of search_products.
from octogen_ai_sdk import Facet, FacetName, OctogenClient

async with OctogenClient() as client:
    catalogs = await client.list_catalogs()
    results = await client.search_products(
        catalog=catalogs[0].catalog,
        q="summer dress",
        facets=[
            Facet(name=FacetName.GENDER, values=["female"]),
            Facet(name=FacetName.COLOR_FAMILY, values=["Blue", "Green"]),
        ],
        price_max=150.0,
        limit=20,
    )
    for product in results.items:
        print(product.title, product.product_url)
You can also pass plain dicts instead of Facet instances — the SDK coerces them automatically:
facets=[
    {"name": "gender", "values": ["female"]},
    {"name": "color_family", "values": ["Blue", "Green"]},
]
FacetName is a StrEnum with constants for all built-in facet fields: BRAND_NAME, GENDER, AGE_GROUPS, COLOR, COLOR_FAMILY, IS_ACTIVEWEAR, PRODUCT_TYPE, CATEGORY_PATH_DEPTH_0 through CATEGORY_PATH_DEPTH_6, and more. You can also pass any custom attribute facet name as a plain string.

Pagination

When next_cursor is not None in a response, pass it as cursor in your next call to retrieve the following page:
cursor = None
while True:
    page = await client.search_products(
        catalog=catalog,
        q="linen dress",
        cursor=cursor,
        limit=50,
    )
    for product in page.items:
        print(product.title)
    if page.next_cursor is None:
        break
    cursor = page.next_cursor

Error handling

All SDK errors inherit from OctogenError. HTTP errors inherit from OctogenAPIError, which exposes status_code, detail, and response attributes.
Raised when your API key is missing, malformed, or has been revoked. Check that OCTO_API_KEY is set correctly and that the key is still active in the partner portal.
Raised when a valid key attempts an action it is not authorized for — for example, accessing a catalog that has not been granted to your organization.
Raised when a catalog or product URL cannot be found. For lookup_product, verify the URL belongs to a catalog your key can access.
Raised when the API rejects a request due to invalid parameters. The detail attribute contains the validation error list from the API.
Raised when the SDK cannot reach the API — for example, due to a network timeout or DNS failure. Does not have a status_code.
Catch the base OctogenAPIError to handle all HTTP errors in one place:
from octogen_ai_sdk import (
    OctogenAPIError,
    OctogenAuthenticationError,
    OctogenConnectionError,
)

try:
    results = await client.search_products(catalog=catalog, q="dress")
except OctogenAuthenticationError:
    print("Check your OCTO_API_KEY.")
except OctogenAPIError as exc:
    print(f"API error {exc.status_code}: {exc.detail}")
except OctogenConnectionError as exc:
    print(f"Network error: {exc}")
MissingAPIKeyError is raised by the constructor, not by a network call. It will surface at client creation time if neither api_key nor OCTO_API_KEY is present.