> For the complete documentation index, see [llms.txt](https://docs.pullbay.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.pullbay.com/documentation/getting-started/first-api-call.md).

# First API Call

Learn how to make your first API call to Pullbay and understand the response structure. This hands-on guide shows you real code examples and explains every step.

## Prerequisites

Before making your first API call, ensure you have:

* A Pullbay account (sign up at [https://dashboard.pullbay.com](https://dashboard.pullbay.com/))
* Your API key copied from the API Keys section of your dashboard
* A tool for making HTTP requests (cURL, Postman, programming language, etc.)
* Basic familiarity with JSON responses

## The Example Request

We'll fetch App Store reviews for Facebook (App Store ID: 284882215) to demonstrate a real API call.

* **Endpoint:** `GET https://api.pullbay.com/appstore/reviews`
* **Parameters:** `appId=284882215` (Facebook's iOS App Store ID)
* **Authentication:** Header `Authorization: Bearer YOUR_API_KEY`

Replace `YOUR_API_KEY` with your actual API key.

{% tabs %}
{% tab title="cURL" %}

```bash
curl -X GET "https://api.pullbay.com/appstore/reviews?appId=284882215" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

{% endtab %}

{% tab title="Python (requests library)" %}

```python
import requests

api_key = "YOUR_API_KEY"
url = "https://api.pullbay.com/appstore/reviews"
params = {"appId": "284882215"}
headers = {"Authorization": f"Bearer {api_key}"}

response = requests.get(url, params=params, headers=headers)
data = response.json()
print(data)
```

{% endtab %}

{% tab title="Node.js (Axios)" %}

```javascript
const axios = require('axios');

const apiKey = 'YOUR_API_KEY';
const url = 'https://api.pullbay.com/appstore/reviews';
const params = { appId: '284882215' };
const headers = { 'Authorization': `Bearer ${apiKey}` };

async function getReviews() {
  try {
    const response = await axios.get(url, { params, headers });
    console.log(response.data);
  } catch (error) {
    console.error('Error:', error.response.data);
  }
}

getReviews();
```

{% endtab %}

{% tab title="Node.js (Native Fetch)" %}

```javascript
const apiKey = 'YOUR_API_KEY';
const url = 'https://api.pullbay.com/appstore/reviews?appId=284882215';

const response = await fetch(url, {
  method: 'GET',
  headers: { 'Authorization': `Bearer ${apiKey}` },
});

const data = await response.json();
console.log(data);
```

{% endtab %}

{% tab title="PHP" %}

```php
<?php
$apiKey = 'YOUR_API_KEY';
$appId = '284882215';

$url = 'https://api.pullbay.com/appstore/reviews?appId=' . $appId;

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer ' . $apiKey,
]);

$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

if ($httpCode === 200) {
    $data = json_decode($response, true);
    print_r($data);
} else {
    echo "Error: HTTP $httpCode\n";
    echo "Response: $response\n";
}
?>
```

{% endtab %}
{% endtabs %}

## Understanding the Response

Here's what a successful response from this API call looks like:

```json
{
  "requestId": "3b8dcb68-1f8c-4a7b-b9e7-63a7b9986f24",
  "status": 200,
  "message": "OK",
  "success": true,
  "data": [
    {
      "id": "10293847561",
      "date": "2026-01-15T10:30:00Z",
      "userName": "HappyUser123",
      "userUrl": "https://itunes.apple.com/us/reviews/id1632846422",
      "version": "5.2.1",
      "score": 5,
      "title": "Great app!",
      "text": "This app has changed how I work. The interface is intuitive and features are powerful. Highly recommended!",
      "url": "https://itunes.apple.com/us/review?id=284882215",
      "country": "US"
    },
    {
      "id": "10293847562",
      "date": "2026-01-14T15:45:00Z",
      "userName": "CriticalReviewer",
      "userUrl": "https://itunes.apple.com/us/reviews/id1632846423",
      "version": "5.2.0",
      "score": 3,
      "title": "Good but needs work",
      "text": "The core features are solid and the performance is decent. However, the app could use better error handling and more customization options.",
      "url": "https://itunes.apple.com/us/review?id=284882215",
      "country": "US"
    },
    {
      "id": "10293847563",
      "date": "2026-01-13T08:15:00Z",
      "userName": "BugHunter42",
      "userUrl": "https://itunes.apple.com/us/reviews/id1632846424",
      "version": "5.2.0",
      "score": 2,
      "title": "Crashes on startup",
      "text": "The app keeps crashing when I try to open it on iOS 17. This happened after the last update. Please fix!",
      "url": "https://itunes.apple.com/us/review?id=284882215",
      "country": "US"
    }
  ],
  "pagination": {
    "page": 1,
    "hasNextPage": true,
    "cursor": null,
    "offset": null
  },
  "pricing": {
    "creditsCharged": 4
  }
}
```

## Response Field Explanations

The response contains a top-level envelope plus `data`, `pagination`, and `pricing`.

**Top level**

| Field       | Type    | Description                                                           |
| ----------- | ------- | --------------------------------------------------------------------- |
| `requestId` | String  | Unique identifier for this request (useful for debugging and support) |
| `status`    | Integer | HTTP status code                                                      |
| `message`   | String  | Human-readable status message                                         |
| `success`   | Boolean | Whether the request succeeded                                         |

### data (Array)

Each review object has these fields:

| Field      | Type     | Description                              |
| ---------- | -------- | ---------------------------------------- |
| `id`       | String   | Unique identifier for this review        |
| `date`     | ISO 8601 | Timestamp when the review was posted     |
| `userName` | String   | Username or display name of the reviewer |
| `userUrl`  | String   | URL to the reviewer's profile            |
| `version`  | String   | App version the reviewer was using       |
| `score`    | Integer  | Star rating given by the reviewer (1–5)  |
| `title`    | String   | Review title or headline                 |
| `text`     | String   | Full text of the review                  |
| `url`      | String   | URL to the review                        |
| `country`  | String   | Country code of the review               |

### pagination (Object)

| Field               | Type    | Description                                                                             |
| ------------------- | ------- | --------------------------------------------------------------------------------------- |
| `page`              | Integer | Current page number                                                                     |
| `hasNextPage`       | Boolean | `true` if more pages are available                                                      |
| `cursor` / `offset` | String  | Used only by cursor-/offset-paginated services (e.g. Google Maps); `null` for App Store |

### pricing (Object)

| Field            | Type    | Description                                                                |
| ---------------- | ------- | -------------------------------------------------------------------------- |
| `creditsCharged` | Integer | API credits consumed by this request (1 per request + 1 per item returned) |

## Fetching More Pages

When `pagination.hasNextPage` is `true`, fetch the next page by incrementing `page`:

{% tabs %}
{% tab title="cURL" %}

```bash
curl -X GET "https://api.pullbay.com/appstore/reviews?appId=284882215&page=2" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

{% endtab %}

{% tab title="Python" %}

```python
# Fetch the first page
response1 = requests.get(url, params=params, headers=headers)
data1 = response1.json()

# Check if more results are available
if data1["pagination"]["hasNextPage"]:
    params["page"] = 2
    response2 = requests.get(url, params=params, headers=headers)
    data2 = response2.json()

    all_reviews = data1["data"] + data2["data"]
    print(f"Fetched {len(all_reviews)} reviews total")
```

{% endtab %}

{% tab title="Node.js" %}

```javascript
// Fetch the first page
const response1 = await axios.get(url, {
  params: { appId: '284882215' },
  headers,
});
const data1 = response1.data;

// Check if more results are available
if (data1.pagination.hasNextPage) {
  const response2 = await axios.get(url, {
    params: { appId: '284882215', page: 2 },
    headers,
  });
  const data2 = response2.data;

  const allReviews = [...data1.data, ...data2.data];
  console.log(`Fetched ${allReviews.length} reviews total`);
}
```

{% endtab %}
{% endtabs %}

Alternatively, use `maxItems` instead of `page` to pull a larger set in a single call (the two are mutually exclusive).

## Check Your Usage in the Dashboard

{% stepper %}
{% step %}

## Sign in

Sign in to <https://dashboard.pullbay.com>
{% endstep %}

{% step %}

## Open Overview

Open your **Overview** to see your remaining credits
{% endstep %}

{% step %}

## View Usage

Click **Usage** to see detailed request history: number of requests, breakdown by endpoint, requests over time, and credits consumed
{% endstep %}
{% endstepper %}

## Common Errors & Troubleshooting

All errors return the standard envelope with `success: false` and an `error` object:

```json
{
  "requestId": "3b8dcb68-1f8c-4a7b-b9e7-63a7b9986f24",
  "status": 400,
  "message": "Bad request",
  "success": false,
  "error": {
    "code": "BAD_REQUEST"
  }
}
```

**400 Bad Request** — Invalid or missing parameters. Verify required parameters are included (e.g. `appId` or `bundleId`) and values are correct (app IDs are numeric strings, 8–12 digits).

**402 Insufficient Credits** — You've used all your available credits. Check your remaining credits in the Overview, then purchase more or upgrade your plan.

**404 Not Found** — The requested resource (e.g. an app or developer ID) couldn't be found. Verify the identifier.

**401 Unauthorized** — Your API key is missing, invalid, or expired. Verify it's copied correctly and sent as `Authorization: Bearer YOUR_API_KEY`. If compromised, revoke it and create a new one.

**429 Too Many Requests** — You've hit a rate limit. Wait before retrying and implement exponential backoff.

**500 / 503 Server Error** — A server-side error or temporary unavailability. Retry after a few seconds; if it persists, contact support with your `requestId`.

### Error Reference Table

| Status    | Message                    | Action                      |
| --------- | -------------------------- | --------------------------- |
| 400       | Bad request                | Verify request parameters   |
| 402       | Insufficient credits       | Purchase more credits       |
| 404       | Not found                  | Verify the identifier       |
| 401       | Unauthorized               | Check your API key          |
| 429       | Too many requests          | Wait and retry with backoff |
| 500 / 503 | Server error / unavailable | Retry or contact support    |

## Next Steps

Congratulations on making your first Pullbay API call! Here's what to explore next:

{% stepper %}
{% step %}

#### [Pagination Guide](/documentation/concepts/pagination.md)

Page-based pagination and bulk pulls with `maxItems`
{% endstep %}

{% step %}

#### [Authentication Guide](/documentation/getting-started/authentication.md)

API keys and security best practices
{% endstep %}

{% step %}

#### [App Store API Reference](/documentation/api-and-references/appstore.md)

Explore all available endpoints and parameters
{% endstep %}

{% step %}

#### [Error Handling](/documentation/support/troubleshooting.md)

Implement robust error handling in your application
{% endstep %}
{% endstepper %}

## FAQ

<details>

<summary>How do I find an app's ID?</summary>

iOS App IDs appear in the App Store URL — for `https://apps.apple.com/app/facebook/id284882215`, the ID is `284882215`. You can also pass a `bundleId` (e.g. `com.burbn.instagram`) instead of `appId`.

</details>

<details>

<summary>What does appId 284882215 refer to?</summary>

`284882215` is the official Apple App Store ID for Facebook. We use it as a demonstration because it's a well-known, widely available app with many reviews. You can replace it with any app ID to fetch reviews for other applications.

</details>

<details>

<summary>Can I fetch reviews for apps on different app stores?</summary>

Yes. Different services serve different stores:

* iOS reviews: `GET /api/appstore/reviews` (use the iOS `appId` or `bundleId`)
* Android reviews: `GET /api/google-play/reviews` (use the package name)

Check the API Reference for endpoint-specific documentation.

</details>

<details>

<summary>How often are reviews updated?</summary>

Reviews are refreshed regularly (typically within 24 hours of being posted). Check your dashboard or contact support for the refresh schedule for your account.

</details>

<details>

<summary>What if I make the same request twice?</summary>

You'll get fresh data, not cached results. Each request consumes credits (1 per request + 1 per item returned) and may return different reviews if new ones have been posted. There's no automatic deduplication, so account for that if you make repeated requests for the same data.

</details>

<details>

<summary>Can I filter reviews by rating or date?</summary>

The App Store reviews endpoint doesn't support rating or date filters. It supports `sort` (`recent` or `helpful`), `country`, and pagination via `page` / `maxItems`. Filtering and sorting options vary by endpoint, so check each endpoint's reference page.

</details>
