# Custom ecommerce integration

The Brand API lets you read your brand's published articles, download their media, and check for publication or retirement changes.

All requests use this base URL:

```text
https://api.creatorgeo.ai/brand/2026-10
```

Ask the Creator GEO team to enable custom ecommerce for your brand and confirm API access. **Published** means an approved article is available through the API; it does not mean the article is live on your storefront.

## Authenticate

Create a key in **Settings → API keys** and set it as the `DELIVERY_API_KEY` environment variable. The full key is shown only when you create it. Send it in every request:

```http
Authorization: Bearer <DELIVERY_API_KEY>
```

The key identifies your brand, so you do not need a brand ID. Keep it out of browser code, source control, and logs. Run the examples from your terminal or backend, with shell tracing turned off.

## API examples

Choose a language to fetch the first page of published articles. Each example makes one request and prints the response.

### cURL

curl.

```sh
# Your Creator GEO API key, stored in DELIVERY_API_KEY.
api_key="$DELIVERY_API_KEY"


# List available articles.
curl --fail --max-time 30 \
  -H "Authorization: Bearer $api_key" \
  'https://api.creatorgeo.ai/brand/2026-10/articles'
```

### Python

Python 3.10+. Install with `pip install requests`.

```python
import os
import requests


# Your Creator GEO API key, stored in DELIVERY_API_KEY.
api_key = os.environ["DELIVERY_API_KEY"]


# List available articles.
response = requests.get(
    "https://api.creatorgeo.ai/brand/2026-10/articles",
    headers={"Authorization": f"Bearer {api_key}"},
    timeout=30,
)
response.raise_for_status()


print(response.json())
```

### Node.js

Node.js 18+; save as example.mjs.

```js
// Your Creator GEO API key, stored in DELIVERY_API_KEY.
const apiKey = process.env.DELIVERY_API_KEY;


// List available articles.
const response = await fetch("https://api.creatorgeo.ai/brand/2026-10/articles", {
  headers: { Authorization: `Bearer ${apiKey}` },
  signal: AbortSignal.timeout(30_000),
});
if (!response.ok) {
  throw new Error(`HTTP ${response.status}`);
}


console.log(await response.json());
```

### Ruby

Ruby 3.1+.

```ruby
require 'net/http'
require 'uri'


# Your Creator GEO API key, stored in DELIVERY_API_KEY.
api_key = ENV.fetch('DELIVERY_API_KEY')


# List available articles.
url = URI('https://api.creatorgeo.ai/brand/2026-10/articles')
request = Net::HTTP::Get.new(url)
request['Authorization'] = "Bearer #{api_key}"


response = Net::HTTP.start(
  url.host, url.port,
  use_ssl: url.scheme == 'https',
  open_timeout: 30, read_timeout: 30
) { |http| http.request(request) }
response.value # Raise on an HTTP error.


puts response.body
```

### Java

JDK 17+; save as Example.java.

```java
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;


public class Example {
  public static void main(String[] args) throws Exception {
    // Your Creator GEO API key, stored in DELIVERY_API_KEY.
    String apiKey = System.getenv("DELIVERY_API_KEY");


    // List available articles.
    HttpClient client = HttpClient.newHttpClient();
    HttpRequest request = HttpRequest.newBuilder()
      .uri(URI.create("https://api.creatorgeo.ai/brand/2026-10/articles"))
      .header("Authorization", "Bearer " + apiKey)
      .timeout(Duration.ofSeconds(30))
      .GET()
      .build();


    HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
    System.out.println(response.body());
  }
}
```

### Go

Go 1.22+; save as example.go.

```go
package main


import (
  "fmt"
  "io"
  "net/http"
  "os"
  "time"
)


func main() {
  // Your Creator GEO API key, stored in DELIVERY_API_KEY.
  apiKey := os.Getenv("DELIVERY_API_KEY")


  // List available articles.
  request, err := http.NewRequest(http.MethodGet, "https://api.creatorgeo.ai/brand/2026-10/articles", nil)
  if err != nil {
    panic(err)
  }
  request.Header.Set("Authorization", "Bearer " + apiKey)


  client := &http.Client{Timeout: 30 * time.Second}
  response, err := client.Do(request)
  if err != nil {
    panic(err)
  }
  defer response.Body.Close()


  body, err := io.ReadAll(response.Body)
  if err != nil {
    panic(err)
  }


  fmt.Println(string(body))
}
```

The response contains article metadata and pagination cursors:

```json
{
  "articles": [
    {
      "id": "10000000-0000-4000-8000-000000000001",
      "title": "A creator's guide to everyday layers",
      "version": 1,
      "published_at": "2026-10-01T12:00:00Z"
    }
  ],
  "next_cursor": null,
  "change_cursor": "opaque-synthetic-change-cursor"
}
```

Only currently published custom-ecommerce articles appear here. Drafts, retired articles, and articles delivered through Shopify are excluded.

## Get the next page

When `next_cursor` is not `null`, pass it as `cursor` in your next request. Replace the example value below with the cursor from your response.

```sh
# Your Creator GEO API key, stored in DELIVERY_API_KEY.
api_key="$DELIVERY_API_KEY"


# Fetch the next page of articles.
curl --fail --max-time 30 --get \
  -H "Authorization: Bearer $api_key" \
  --data-urlencode 'cursor=opaque-synthetic-list-cursor' \
  'https://api.creatorgeo.ai/brand/2026-10/articles'
```

The default page size is 50. Add `limit` to request between 1 and 100 articles. Treat cursors as opaque values: pass them back unchanged using your client's query-parameter support.

## Read an article

Use an article ID from the list response:

```sh
# Your Creator GEO API key, stored in DELIVERY_API_KEY.
api_key="$DELIVERY_API_KEY"


# Fetch an article's content.
curl --fail --max-time 30 \
  -H "Authorization: Bearer $api_key" \
  'https://api.creatorgeo.ai/brand/2026-10/articles/10000000-0000-4000-8000-000000000001'
```

The response includes `body_html`, `head_title`, `meta_description`, `canonical_url`, `stylesheet`, and a `media` list. It is the current approved snapshot. See the [API reference](https://docs.creatorgeo.ai/api/reference/index.md) for the complete response schema and examples in all six languages.

## Download media

Each media entry has a `download_path`. Add it to `https://api.creatorgeo.ai` and send the same authorization header:

```sh
# Your Creator GEO API key, stored in DELIVERY_API_KEY.
api_key="$DELIVERY_API_KEY"


# Download a media file.
curl --fail --max-time 30 \
  -H "Authorization: Bearer $api_key" \
  --output example-media.png \
  'https://api.creatorgeo.ai/brand/2026-10/articles/10000000-0000-4000-8000-000000000001/media/20000000000040008000000000000001'
```

Use `HEAD` to read the headers without downloading the file:

```sh
# Your Creator GEO API key, stored in DELIVERY_API_KEY.
api_key="$DELIVERY_API_KEY"


# Read media headers only.
curl --fail --max-time 30 --head \
  -H "Authorization: Bearer $api_key" \
  'https://api.creatorgeo.ai/brand/2026-10/articles/10000000-0000-4000-8000-000000000001/media/20000000000040008000000000000001'
```

Media requests return `200` for the complete file. A single `Range` header can request part of a file, returning `206`. Media requires the API key; the download URL is not a public storefront URL.

## Check whether an article changed

Article responses include an `ETag` header. Send that value as `If-None-Match` on a later request:

```sh
# Your Creator GEO API key, stored in DELIVERY_API_KEY.
api_key="$DELIVERY_API_KEY"


# Check a previously fetched article.
curl --fail --max-time 30 --include \
  -H "Authorization: Bearer $api_key" \
  -H 'If-None-Match: "1111111111111111111111111111111111111111111111111111111111111111"' \
  'https://api.creatorgeo.ai/brand/2026-10/articles/10000000-0000-4000-8000-000000000001'
```

Replace the example ETag with the one you received. A `304 Not Modified` response has no body and lets you reuse your complete saved response. A `200` response contains the current article. Revoked keys and retired articles are still refused.

## Read publication changes

Use the article list's `change_cursor` to request changes since that list began:

```sh
# Your Creator GEO API key, stored in DELIVERY_API_KEY.
api_key="$DELIVERY_API_KEY"


# Fetch publication and retirement changes.
curl --fail --max-time 30 --get \
  -H "Authorization: Bearer $api_key" \
  --data-urlencode 'cursor=opaque-synthetic-change-cursor' \
  'https://api.creatorgeo.ai/brand/2026-10/article-changes'
```

The response contains a `changes` array, `next_cursor`, and `change_cursor`. Events arrive in increasing `sequence` order:

-   `published` identifies an article whose current approved content you can fetch.
-   `retired` identifies an article that is no longer available. Detail and media requests for it return `410`.

Follow `next_cursor` until it is `null`. After reading all pages, use `change_cursor` to request later changes. Omitting `cursor` reads from the beginning of the change history. The default page size is 100; `limit` accepts 1–500.

List cursors and change cursors are different; use each only with its own endpoint. An article missing from a list is not a retirement event.

## Response codes

| Code | Meaning |
| --- | --- |
| `400` | Invalid limit or cursor. |
| `401` | Missing, invalid, or revoked API key. |
| `404` | Article or media is unavailable to this key. |
| `410` | The article has been retired. |
| `416` | Invalid or unsatisfiable media range. |
| `429` | Request limit reached; `Retry-After` gives the wait in seconds. |
| `5xx` | Server error. Try the request again later. |

The current limit is 120 requests per minute per brand per API process, shared across the brand's keys and endpoints. All API responses use `Cache-Control: private, no-store`.

The `/2026-10` path identifies the API contract version. Unsupported versions return `404` without a redirect or fallback. For every endpoint, parameter, and response field, see the [Brand API reference](https://docs.creatorgeo.ai/api/reference/index.md) or [download the OpenAPI specification](https://docs.creatorgeo.ai/openapi/brand-2026-10.yaml).
