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

# How do I connect to the Shopify GraphQL API in Parabola?

> Set up a custom Shopify integration in Parabola using your own app credentials to query the Shopify GraphQL API.

This guide walks you through setting up a custom Shopify integration in Parabola using your own Shopify app credentials. You'll create an app in Shopify's Dev Dashboard, configure it to work with Parabola, and authorize a permanent connection so your flows run automatically in the background without interruption.

Before you begin, make sure you have:

* A Shopify account with app development permissions
* A Parabola account with a flow open and ready to connect

## Set up your app in Shopify

### Step 1: Create a new app

<Steps>
  <Step title="Log in to your Shopify account and navigate to the Dev Dashboard">
    Go to the [Shopify Dev Dashboard](https://dev.shopify.com/dashboard/).
  </Step>

  <Step title="Create the app">
    1. In the left-hand navigation, click **Apps**
    2. Click **Create app** in the top-right corner
    3. Select **Start from Dev Dashboard**
    4. Name your app (e.g., "Parabola Integration") and click **Create**
  </Step>
</Steps>

### Step 2: Create a version

<Steps>
  <Step title="Configure version settings">
    1. From your app, click the **Versions** tab
    2. Set your App URL to: `https://shopify.dev/apps/default-app-home`
    3. Select the newest **Webhooks API version**
  </Step>

  <Step title="Add scopes">
    Under **Scopes**, add the permissions your integration needs. Common examples:

    | Scope            | What it allows                   |
    | ---------------- | -------------------------------- |
    | `read_orders`    | Pull order data into Parabola    |
    | `read_products`  | Pull product data into Parabola  |
    | `read_customers` | Pull customer data into Parabola |
    | `read_reports`   | Submit ShopifyQL queries         |
    | `read_analytics` | Submit ShopifyQL queries         |
  </Step>

  <Step title="Add the redirect URL and release">
    1. Under **Redirect URLs**, add: `https://parabola.io/api/steps/generic_api/callback`
    2. Click **Release**
  </Step>
</Steps>

<Info>
  **Offline access:** When selecting scopes, you're granting Parabola permission to access your store in the background without you being actively logged in. In step 6 below, you'll request a non-expiring token to make sure this access never interrupts your automated flows.
</Info>

### Step 3: Install your app on your store

1. From the Dev Dashboard, scroll down on your app and click **Install app**
2. Select your store and click **Install**

### Step 4: Get your credentials

1. From the Dev Dashboard, click **Settings**
2. Copy your **Client ID** and **Client Secret** — you'll need both in Parabola

## Connect Shopify to Parabola

### Step 5: Add an API step in Parabola

1. In your Parabola flow, add a **Pull from API** step to import Shopify data
2. In the step settings, open the Authentication dropdown and select **Add expiring access token**

### Step 6: Configure the expiring access token

<Steps>
  <Step title="Set the access token request URL">
    In the **Access Token Request URL (POST)** field, enter:

    ```text theme={null}
    https://{your-store}.myshopify.com/admin/oauth/authorize
    ```

    Replace `{your-store}` with your Shopify store name.
  </Step>

  <Step title="Add request body parameters">
    Add the following parameters:

    * `client_secret` → your Client Secret
    * `client_id` → your Client ID
    * `grant_type` → `client_credentials`
  </Step>

  <Step title="Configure request headers">
    * Set **Content-Type** to `application/x-www-form-urlencoded`
    * Delete the **Accept** header
  </Step>

  <Step title="Configure token settings">
    * **Response Access Token Field** → `access_token`
    * **Header Key for using Access Token** → `X-Shopify-Access-Token`
    * **Header Value for using Access Token** → `{token}`
  </Step>

  <Step title="Apply the settings">
    Click **Apply Access Token settings**.

    <Accordion title="Your configuration should look like this:">
      <Frame>
        <img src="https://mintcdn.com/parabola-7119dfb0/TsbrP5Ojj3Ado2i1/images/shopify-graphql-access-token-settings.png?fit=max&auto=format&n=TsbrP5Ojj3Ado2i1&q=85&s=ddb257d3b3a26af005604028517f6317" alt="Shopify access token configuration in Parabola showing request body parameters, request headers, and token settings" width="1054" height="1542" data-path="images/shopify-graphql-access-token-settings.png" />
      </Frame>
    </Accordion>
  </Step>
</Steps>

### Step 7: Pull from the GraphQL API

<Steps>
  <Step title="Configure the API request">
    1. Select the pen icon near **API Request Settings**
    2. Switch the **API Endpoint URL** method to **POST**
    3. Enter the endpoint URL:

    ```text theme={null}
    https://{your-development-store}.myshopify.com/admin/api/2026-04/graphql.json
    ```

    Replace `{your-development-store}` with your Shopify store name.
  </Step>

  <Step title="Add a GraphQL query">
    1. Select **GraphQL** for the **Request Body**
    2. Paste the following sample query:

    ```graphql theme={null}
    query {
      orders(first: 10) {
        edges {
          cursor
          node {
            id
          }
        }
        pageInfo {
          hasNextPage
          hasPreviousPage
          startCursor
          endCursor
        }
      }
    }
    ```

    Customize this query based on the Shopify data you need to retrieve.
  </Step>
</Steps>

## Use cursor pagination to pull more records

Shopify's Admin GraphQL API caps how many records you can fetch in a single request (typically 250). To pull more, you must page through results using **cursors** — opaque tokens that point to the next page. Without pagination, your flow only ever sees the first page.

### How cursor pagination works in Shopify

Shopify paginates connection fields using `first` + `after`:

1. Request page 1 with `first: N` and no `after` (or `after: null`).
2. Shopify returns:
   * `pageInfo.hasNextPage` — whether more pages exist
   * `pageInfo.endCursor` — the token for the next page
3. Request page 2 by sending `after: "<endCursor from page 1>"`.
4. Repeat until `hasNextPage` is `false`.

Minimal pattern:

```graphql theme={null}
orders(first: 250, after: "<cursor>") {
  nodes { id name createdAt }
  pageInfo {
    hasNextPage
    endCursor
  }
}
```

### How Parabola handles the loop

In the **Pull from API** step, Parabola swaps in a placeholder each request as it loops through pages.

| Setting                          | What it does                                                                      |
| -------------------------------- | --------------------------------------------------------------------------------- |
| **Pagination style**             | Set to `Cursor`                                                                   |
| **Cursor key**                   | The placeholder name in your query body (default: `cursor`, used as `<%cursor%>`) |
| **Cursor path in response**      | Where Parabola reads the next cursor from the response                            |
| **hasNextPage path**             | Where Parabola checks if it should keep paging                                    |
| **Pages to fetch while editing** | Page cap when previewing in the editor (keep low, e.g., 5–200)                    |
| **Pages to fetch while running** | Page cap on full runs (e.g., 500+)                                                |

Parabola's loop:

1. Send the request (cursor starts empty / `null`).
2. Read `hasNextPage` at the configured path.
3. Read `endCursor` and substitute it into `<%cursor%>` for the next request.
4. Stop when `hasNextPage` is `false` or the page cap is hit.

### Step-by-step setup

<Steps>
  <Step title="Write the GraphQL query with <%cursor%> in the after argument">
    ```graphql theme={null}
    query getOrders {
      orders(first: 250, after: "<%cursor%>") {
        nodes {
          id
          name
          createdAt
          totalPriceSet { shopMoney { amount currencyCode } }
        }
        pageInfo {
          hasNextPage
          endCursor
        }
      }
    }
    ```

    <Note>The first request will send `after: ""`. Shopify treats an empty string as "start from the beginning," so no special handling is needed.</Note>
  </Step>

  <Step title="Configure the pagination panel">
    Using the example query above (`data.orders`), the settings are:

    | Field                        | Value                              |
    | ---------------------------- | ---------------------------------- |
    | Pagination style             | `Cursor`                           |
    | Cursor key                   | `cursor`                           |
    | Cursor path in response      | `data.orders.pageInfo.endCursor`   |
    | hasNextPage path             | `data.orders.pageInfo.hasNextPage` |
    | Pages to fetch while editing | `200`                              |
    | Pages to fetch while running | `500`                              |
  </Step>

  <Step title="Run the flow">
    Parabola will keep firing requests until `hasNextPage` is `false` or the page cap is hit, concatenating results into a single output table.
  </Step>
</Steps>

### Adapting the paths to your query

The response paths must match the **shape of your GraphQL response**, which mirrors the shape of your query.

**Example A — `orders`**

* Query root: `orders { ... pageInfo { ... } }`
* Cursor path in response: `data.orders.pageInfo.endCursor`
* hasNextPage path: `data.orders.pageInfo.hasNextPage`

**Example B — `shopifyPaymentsAccount.balanceTransactions`**

* Query root: `shopifyPaymentsAccount { balanceTransactions { ... pageInfo { ... } } }`
* Cursor path in response: `data.shopifyPaymentsAccount.balanceTransactions.pageInfo.endCursor`
* hasNextPage path: `data.shopifyPaymentsAccount.balanceTransactions.pageInfo.hasNextPage`

```graphql theme={null}
query getBalanceTxns {
  shopifyPaymentsAccount {
    balanceTransactions(first: 100, after: "<%cursor%>") {
      nodes {
        id
        type
        amount { amount currencyCode }
        transactionDate
      }
      pageInfo {
        hasNextPage
        endCursor
      }
    }
  }
}
```

**Example C — `products`**

* Cursor path in response: `data.products.pageInfo.endCursor`
* hasNextPage path: `data.products.pageInfo.hasNextPage`

<Tip>The path is always `data.<your_top_level_field>.pageInfo.endCursor` (or `.hasNextPage`).</Tip>

### Common pitfalls

* **Wrong paths.** If `data.orders.pageInfo.endCursor` doesn't match your actual response shape, Parabola will only pull page 1. Run the query once, inspect the response, and confirm the path before turning on pagination.
* **Forgot `<%cursor%>` in the query.** The placeholder must appear in the `after:` argument. Without it, Parabola has nowhere to inject the cursor.
* **Paginating a non-connection field.** Only fields that return `pageInfo` (connections) support cursor pagination. Singletons like `shop { ... }` do not.
* **Editing page cap too low.** If you cap at 5 pages while editing, your preview will look truncated — that's expected. The run-time cap is what matters for production.
* **Using `first` larger than Shopify allows.** Most connections cap at 250. Going higher returns an error.
* **Rate limits.** Long pagination loops can hit Shopify's cost-based rate limit. If you see throttling, lower `first` or add a delay.

For more details, see [Shopify's GraphQL pagination docs](https://shopify.dev/docs/api/usage/pagination-graphql).

## Need to add more scopes?

If you need to grant Parabola access to additional Shopify data after your initial setup, you can update your app's scopes without disrupting your existing connection.

1. Click on your app in the **Dev Dashboard**
2. Click on the **Versions** tab
3. Click **Create Version** in the upper right
4. Select **Scopes** under Add Access and add the ones listed [above](#step-2-create-a-version)

Your client ID and client secret should not change, so there should be no disruption to your existing connection.
