---
title: "Build with AI"
description: "Point Claude, Cursor, or other AI clients at the Pine59 docs and API to list datasets, query metrics, and explain results without writing glue code."
slug: "/docs/guides/build-with-ai"
last_updated: "2026-08-31"
status: "published"
industry: []
location_types: []
tags: ["get-started", "api", "ai"]
is_case_study: false
locked: false
related_metrics: []
related_datasets: []
example_count: 0
example_tools: []
---


# Build with AI

Point Claude, Cursor, or other AI clients at the Pine59 docs and API to list datasets, query metrics, and explain results without writing glue code.

## Metadata

- Tags: get-started, api, ai

An AI agent pointed at the Pine59 API can pick the right dataset, build the request, run it, and explain the result back in plain English.

> **The short version:** point your AI client at these docs. Every page has an LLM-readable Markdown mirror (indexed at [/llms.txt](/llms.txt)), and the API is described by an OpenAPI spec, so an agent can learn the catalog and request shapes. Keep the API key out of the chat. Put it in an environment variable (or in the client's auth settings) so the agent can call the API without ever seeing the secret. The setup differs per client: Claude-based clients work out of the box, ChatGPT needs a one-time Custom GPT Action setup, and any chat-only client works with the copy-paste fallback.

---

## Before you start

You need:

- An **API key**: if you do not have one yet, follow [Make your first query](/docs/guides/first-query) (steps 1 and 2) to create one.
- An **AI client**. What "works out of the box" depends on which one you use:

| Client | How it actually reaches Pine59 | Setup |
| --- | --- | --- |
| **Claude Code / Cursor in agent mode** | Runs `curl` directly in your shell | Put the system prompt below in `CLAUDE.md` or `.cursorrules` |
| **Claude Desktop** | Built-in web-fetch tools | Paste the system prompt at the top of a new chat |
| **ChatGPT desktop / chatgpt.com** | Needs a **Custom GPT with Actions**, because basic chat and Code Interpreter cannot reach the internet | One-time Custom GPT setup (section 3) |
| **Anything chat-only** (claude.ai free tier, etc.) | The agent writes the `curl`, you paste back the response | No setup; copy-paste between terminal and chat |

---

## 1. Give the agent your key safely

Do not paste the key into the chat: it is stored in the client's conversation history. Put it in an environment variable instead:

```bash
export PINE59_API_KEY="uc_paste_your_key_here"
```

For Claude Code / Cursor / any agentic shell, that is all. The agent can read `$PINE59_API_KEY` from the same shell it runs `curl` in.

For ChatGPT Custom GPT Actions, the key is entered once during Action setup and stored encrypted server-side; it never appears in the conversation transcript.

For chat-only clients that cannot read environment variables, paste the key once at the start of the session and delete the chat afterwards.

---

## 2. Drop this into the agent's instructions

Paste the block below into your agent's system prompt, project rules (`CLAUDE.md`, `.cursorrules`), Custom GPT instructions, or the first message of a new conversation. It is small on purpose. The agent fetches the rest from the docs and the OpenAPI spec itself.

```text
You have access to the Pine59 Catalog API.

Spec: https://api.pine59.com/openapi.json
Base URL: https://api.pine59.com
Docs: https://console.pine59.com/llms.txt indexes a markdown mirror of
every docs page — coverage per country, dataset schemas, methodology.
Fetch it when you need to know what data exists or what a column means.

Authentication: send "Authorization: Bearer <key>" on every request.
The key lives in the PINE59_API_KEY environment variable (or comes
from the Action's configured auth, in ChatGPT). Never echo the key
back in your output.

Key endpoints:
- GET  /v3/datasets
    List the datasets my account has access to. Use this first when I
    ask about coverage or you don't know which dataset to use.
- GET  /v3/datasets/{dataReferenceName}
    Get a dataset's schema, fields, and metadata.
- POST /v3/datasets/{dataReferenceName}:runQuery
    Query rows. Body shape:
      fields:     [{ name, aggregation? }]   (aggregation = SUM|AVG|MIN|MAX|COUNT|COUNT_DISTINCT)
      filters:    [{ fieldName, operator, value }]   (operators: == != > < >= <= in "not in")
      groupBy:    [{ fieldName }]
      pageSize, pageToken, filterLogic
    All values come back as strings inside a flat "fields" map. Aggregated
    result keys are named "{fieldName}_{AGGREGATION}" e.g. "visits_sum_SUM".
- POST /v3/datasets/{dataReferenceName}:searchFieldValues
    List distinct values present in a field. Always call this before
    building a filter on a column you haven't seen values for —
    otherwise you'll filter on something that doesn't exist and the
    result will be silently empty.

When I ask a question, follow this loop:
  1. If you don't know which dataset answers it, GET /v3/datasets and
     pick one based on names and descriptions.
  2. GET the dataset to confirm its schema and the field you intend to
     filter / aggregate on.
  3. searchFieldValues on each field you plan to filter, to verify the
     value I gave you actually exists.
  4. runQuery with the filter / aggregation / groupBy.
  5. Summarise the result in plain English. Show me the curl you ran.

If a request returns 401, the key is wrong. If 403 / NotFound on a
dataset, my account doesn't have access — pick a different one and
tell me. If a query returns 0 rows, re-check filter values with
searchFieldValues before assuming "no data".
```

---

## 3. Setting up ChatGPT specifically

ChatGPT is the one client that needs a one-time configuration before it can reach Pine59. Both basic chat and Code Interpreter run in sandboxes with **no outbound network access**. They report "I cannot reach external endpoints" and stop. The fix is to add Pine59 as a **Custom GPT Action**, which goes through OpenAI's HTTP edge instead of the sandbox.

### Create the Custom GPT

1. Open [chatgpt.com/gpts/editor](https://chatgpt.com/gpts/editor) → **Create**.
2. Skip the conversational setup and click **Configure**.
3. Name it (e.g. "Pine59"). Paste the system prompt from section 2 into the **Instructions** field.
4. Scroll to **Actions** → **Create new action**.
5. Under **Schema**, paste a ChatGPT-compatible OpenAPI spec (see next section). The raw URL `https://api.pine59.com/openapi.json` will fail validation as-is.
6. Under **Authentication**, pick **API key** → Auth Type **Bearer** → paste your `uc_…` key.
7. Add `https://www.pine59.com/privacy` as the privacy-policy URL (required to save).
8. **Save** the Action, then **Save** the GPT.

Now in chat: *"List 5 datasets I have access to"* / *"Run a query on `visitation_usa_places.foot_traffic_month`, total `visits_sum` for `location_id` `<id>` in Jan 2025"*. ChatGPT calls the right endpoint natively and renders the JSON as a Markdown table.

### Spec patches

If you point Custom GPT at the hosted spec URL, ChatGPT rejects it with three errors. Apply these patches to a local copy and paste the result into the Schema field:

| Error | Fix |
| --- | --- |
| `Input should be '3.1.1' or '3.1.0'` (top-level `openapi`) | Change `"openapi": "3.0.0"` → `"openapi": "3.1.0"` |
| `runQuery description has length 385 exceeding limit of 300` | Trim the description on `POST /v3/datasets/{dataReferenceName}:runQuery` to ≤ 300 characters |
| `v3DeleteRecordsExportResponse object schema missing properties` | Add `"properties": {}, "additionalProperties": true` to that schema (or drop the `/v3/exports/*` paths entirely if you do not need exports from chat) |

Optional but recommended: also strip the `/v3/exports/*` endpoints from your trimmed spec. Bulk-export jobs do not fit "ask in chat" use cases and removing them keeps the GPT's tool list focused on the four read endpoints.

> The hosted spec needs these patches for ChatGPT's importer as of August 2026. Host the patched JSON anywhere ChatGPT can reach it (a private GitHub gist works) or paste it into the Schema field directly.

---

## 4. Ask a question

Once the prompt is in place and the key lives outside the chat (`$PINE59_API_KEY` in the shell, or the client's auth settings), ask in plain English:

> *"How many visits did `visitation_usa_places.foot_traffic_month` show for location `<some_location_id>` in January 2025?"*

The agent should:

1. Hit `GET /v3/datasets/visitation_usa_places.foot_traffic_month` to confirm the schema has `location_id`, `observation_start_date`, `observation_end_date`, and `visits_sum`.
2. Call `searchFieldValues` on `location_id` to verify your ID exists.
3. Run the query:

   ```bash
   curl -X POST "https://api.pine59.com/v3/datasets/visitation_usa_places.foot_traffic_month:runQuery" \
     -H "Authorization: Bearer $PINE59_API_KEY" \
     -H "Content-Type: application/json" \
     -d '{
       "fields": [
         { "name": "visits_sum", "aggregation": "SUM" }
       ],
       "filters": [
         { "fieldName": "location_id", "operator": "==", "value": "<some_location_id>" },
         { "fieldName": "observation_start_date", "operator": ">=", "value": "2025-01-01" },
         { "fieldName": "observation_end_date",   "operator": "<=", "value": "2025-01-31" }
       ],
       "pageSize": 1
     }'
   ```

4. Read the result key (`visits_sum_SUM`) and report back in plain English: *"That location had 138,296 total visits across January 2025."*

Other questions that work well with this setup:

- *"Which datasets do I have access to for the UK?"* → `GET /v3/datasets`, filter by name
- *"Find the location_id for the Starbucks at 230 Park Ave, NYC."* → `searchFieldValues` on a places dataset with `term: "Starbucks"`
- *"Compare monthly visits across these five Norwegian locations for Q4 2025."* → `runQuery` with `groupBy: [{ fieldName: "location_id" }, { fieldName: "observation_start_date" }]`

---

## 5. Verify the agent's work

A few things to spot-check when you start using a new agent:

- **It runs the request rather than guessing.** Make the agent show you the `curl` command it sent. If it stops doing that, ask explicitly: *"Show me the request you ran."*
- **It checks field values.** If the agent jumps straight to a filter on a column it has not looked up, totals will be wrong silently. The system prompt above tells it to use `searchFieldValues` first. If it skips that step, remind it.
- **It reads the aggregated key correctly.** Aggregated results come back as `{fieldName}_{AGGREGATION}` (e.g. `visits_sum_SUM`), not the original column name. Agents sometimes look for the wrong key and report "no result".

---

## Sharing a Custom GPT with your team

Before you share it: **a Custom GPT with API-key Actions uses the creator's key for every user**. There is no per-user split. If you share the GPT with the company, every conversation hits Pine59 as *you*: your billing account, your rate limits, your access scope, no per-user attribution in our logs.

That is workable for internal sharing among colleagues you trust; treat the key like a shared service-account credential and rotate it regularly. It is not suitable for the GPT store or for handing the GPT to customers.

To get per-user isolation: **OAuth on Pine59**. Custom GPT Actions support OAuth 2.0, so each user clicks "Sign in with Pine59" and gets a scoped, per-user token. This requires backend work on Pine59's side and is on the roadmap; contact support if you need it sooner. Until then, the simplest per-user setup is each colleague creating their own key and their own client config.

---

## Tips

- **Cache the spec.** `https://api.pine59.com/openapi.json` is ~55 KB. Most agents refetch it every conversation; you can save it locally and point the agent at the file path to save round-trips.
- **Keep the system prompt short.** Agents work better when instructions fit in a few hundred tokens. The block in section 2 is intentionally small, so let the agent learn the rest from the spec.
- **For repeated workflows, codify them.** Once you have found a question that works well, write the final `curl` (or the agent's plan) into a `CLAUDE.md` / `.cursorrules` / Custom GPT instructions so it is reproducible.

---

## Where to go next

- **[Make your first query](/docs/guides/first-query)**: the manual version of what the agent is doing under the hood.
- **[API reference](/docs/api)**: same endpoints, with an interactive "Try it" panel and full field documentation.
- **[Datasets](/docs/datasets)**: what data the agent can ask for, broken down by country.

For anything the docs do not cover, email [support@pine59.com](mailto:support@pine59.com).
