# GlobeData Reseller API

> **Browsable version:** the same reference with copy-paste cURL / Python / Node.js examples, use-case
> guidance and full recipes lives at **<https://globedata.io/api-docs.html>**.

Programmatic access to your sub-user pool. This is the same surface area you see in the **Sub-Users** tab in the dashboard, exposed as a versioned REST API.

- **Base URL:** `https://api.globedata.io/api/v1/reseller`
- **Auth:** API key in the `Authorization` header
- **Format:** JSON request/response, `Content-Type: application/json`
- **Rate limit:** 60 requests / minute / key

---

## 1. Authentication

Every request must include a Bearer token containing your API key:

```
Authorization: Bearer gd_<32 alphanumeric chars>
```

Generate, list, and revoke keys from the **Sub-Users → API Keys** panel in the dashboard. The full key is shown **once** at creation; we store only a SHA-256 hash, so we can't recover a lost key — create a new one instead.

A key inherits the permissions of the reseller it belongs to. It can manage that reseller's sub-users and nothing else.

### Auth errors

| Status | Reason |
|---|---|
| `401 Unauthorized` | Header missing, key malformed, key revoked, key not found, account inactive, or key owner is not a reseller |
| `429 Too Many Requests` | More than 60 requests in the last 60 seconds for this key |

```json
{ "error": "Unauthorized", "message": "API key has been revoked" }
```

---

## 2. Endpoints

### 2.1 GET `/summary` — pool overview

Returns each of your active parent packages with allocated / used / free GB and how many sub-users are drawing from it.

```bash
curl -H "Authorization: Bearer $GD_KEY" \
  https://api.globedata.io/api/v1/reseller/summary
```

```json
{
  "success": true,
  "hard_cap": 200,
  "packages": [
    {
      "user_package_id": 4,
      "package_name": "Custom 2GB Package",
      "gb_total": 2.00,
      "gb_used": 0.04,
      "gb_allocated_to_subs": 1.16,
      "gb_free": 0.80,
      "subuser_count": 2,
      "expiry_date": "2027-01-29T20:18:53.499Z"
    }
  ]
}
```

### 2.2 GET `/subusers` — list sub-users

Returns every sub-user under your account with credentials, allocated/used/remaining GB, status, expiry, and IP allowlist.

```bash
curl -H "Authorization: Bearer $GD_KEY" \
  https://api.globedata.io/api/v1/reseller/subusers
```

```json
{
  "success": true,
  "hard_cap": 200,
  "count": 1,
  "subusers": [
    {
      "id": "3fbe257e-b343-4a37-a46b-f9f0e7bb39d6",
      "email": "customer@example.com",
      "label": "ACME Corp",
      "user_package_id": 46,
      "parent_user_package_id": 4,
      "proxy_username": "sub_8j6hVeUj",
      "proxy_password": "DHczgmgZksQNvpKx",
      "is_active": true,
      "gb_total": 0.16,
      "gb_used": 0.00004,
      "gb_remaining": 0.16,
      "ip_allowlist": null,
      "expiry_date": "2027-01-29T20:18:53.499Z",
      "is_expired": false,
      "last_used_at": "2026-05-03T22:03:36.044Z",
      "created_at": "2026-05-03T22:01:50.123Z"
    }
  ]
}
```

### 2.3 POST `/subusers` — create a sub-user

Required body fields: `email`, `dashboard_password`, `gb_initial`, `parent_user_package_id`.
Optional: `label`, `expiry_date` (ISO 8601, capped at parent's expiry), `ip_allowlist` (array of IPs).

`dashboard_password` is what your customer will use to log into the GlobeData dashboard. Min 8 chars.

```bash
curl -X POST -H "Authorization: Bearer $GD_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "customer@example.com",
    "dashboard_password": "verysecret123",
    "label": "ACME Corp",
    "gb_initial": 5,
    "parent_user_package_id": 4,
    "expiry_date": "2026-12-31T23:59:59Z"
  }' \
  https://api.globedata.io/api/v1/reseller/subusers
```

`201 Created`:
```json
{
  "success": true,
  "subuser": {
    "id": "...",
    "email": "customer@example.com",
    "label": "ACME Corp",
    "user_package_id": 47,
    "proxy_username": "sub_<random>",
    "proxy_password": "<random16>",
    "gb_total": 5,
    "expiry_date": "2026-12-31T23:59:59.000Z",
    "auth0_user_id": "auth0|..."
  }
}
```

The proxy credentials shown here are for proxy traffic; the dashboard credentials are the email + the password you sent in the request body. Both are needed by the customer.

Errors:
- `400` validation failure
- `403` reached the **200 active sub-users** cap
- `409` email already in use, parent package expired, or `gb_initial` exceeds free GB on parent

### 2.4 PATCH `/subusers/:id` — update a sub-user

All fields optional; supply only what you want to change.

| Field | Effect |
|---|---|
| `label` | Friendly name in your dashboard |
| `ip_allowlist` | Array of IPs (or `null` to clear). Enforcement ships in P2. |
| `expiry_date` | ISO 8601, capped at parent's expiry, must be in the future |
| `is_active` | `false` suspends (also blocks the dashboard login via Auth0); `true` resumes |
| `regenerate_proxy_password` | `true` to issue a fresh 16-char proxy password. The old password stops working immediately. |

```bash
# Suspend a sub-user
curl -X PATCH -H "Authorization: Bearer $GD_KEY" \
  -H "Content-Type: application/json" \
  -d '{"is_active": false}' \
  https://api.globedata.io/api/v1/reseller/subusers/<id>

# Rotate proxy password
curl -X PATCH -H "Authorization: Bearer $GD_KEY" \
  -H "Content-Type: application/json" \
  -d '{"regenerate_proxy_password": true}' \
  https://api.globedata.io/api/v1/reseller/subusers/<id>
```

### 2.5 POST `/subusers/:id/allocate` — top up or reclaim GB

`gb_delta` is positive to top up, negative to reclaim. You can reclaim up to the unused remaining GB on that sub-user.

```bash
# Add 2 GB
curl -X POST -H "Authorization: Bearer $GD_KEY" \
  -H "Content-Type: application/json" \
  -d '{"gb_delta": 2}' \
  https://api.globedata.io/api/v1/reseller/subusers/<id>/allocate

# Reclaim 1 GB
curl -X POST -H "Authorization: Bearer $GD_KEY" \
  -H "Content-Type: application/json" \
  -d '{"gb_delta": -1}' \
  https://api.globedata.io/api/v1/reseller/subusers/<id>/allocate
```

### 2.6 DELETE `/subusers/:id` — soft-delete a sub-user

Disables the sub-user's dashboard login (via Auth0), deactivates their proxy credentials, returns unused GB to your parent pool, and hides the row from list views. Usage history is retained for audit.

```bash
curl -X DELETE -H "Authorization: Bearer $GD_KEY" \
  https://api.globedata.io/api/v1/reseller/subusers/<id>
```

### 2.7 GET `/subusers/:id/logs` — recent request log

Latest N usage records (most recent first). `limit` defaults to 50, max 500.

```bash
curl -H "Authorization: Bearer $GD_KEY" \
  "https://api.globedata.io/api/v1/reseller/subusers/<id>/logs?limit=200"
```

```json
{
  "success": true,
  "limit": 200,
  "count": 10,
  "logs": [
    {
      "request_time": "2026-05-03T22:03:36.044Z",
      "endpoint": "ipinfo.io:443",
      "status_code": 200,
      "error_message": null,
      "kb_used": 6,
      "proxy_used": "residential"
    }
  ]
}
```

### 2.8 GET `/subusers/:id/usage` — daily usage aggregate

Daily aggregates for the last `days` days (default 30, max 365).

```bash
curl -H "Authorization: Bearer $GD_KEY" \
  "https://api.globedata.io/api/v1/reseller/subusers/<id>/usage?days=30"
```

```json
{
  "success": true,
  "days": 30,
  "usage": [
    {
      "date": "2026-05-03",
      "kb": 45,
      "mb": "0.04",
      "gb": "0.0000",
      "requests": 10,
      "successful": 10,
      "failed": 0
    }
  ]
}
```

---

## 3. Using sub-user proxy credentials

Each sub-user gets its own proxy username + password. Pass them through the standard GlobeData proxy:

```bash
# Default (worldwide rotating)
curl -x "http://sub_8j6hVeUj:DHczgmgZksQNvpKx@proxy.globedata.io:8080" https://api.ipify.org

# Country-targeted (US exit)
curl -x "http://sub_8j6hVeUj-country-US:DHczgmgZksQNvpKx@proxy.globedata.io:8080" https://api.ipify.org

# Sticky session for 10 minutes
curl -x "http://sub_8j6hVeUj-session-myid123-ttl-600:DHczgmgZksQNvpKx@proxy.globedata.io:8080" https://api.ipify.org
```

GB consumed by proxy traffic is decremented from the sub-user's package and reflected in `/subusers/:id/usage`.

---

## 4. Errors

All errors return JSON with at least `error` and `message`:

```json
{ "error": "Conflict", "message": "Not enough free GB on parent package (free: 0.80 GB)" }
```

| Status | Meaning |
|---|---|
| `400` | Validation failure — see `details` array |
| `401` | Auth (see §1) |
| `403` | Cap reached, sub-users can't perform this action |
| `404` | Sub-user not found, or it doesn't belong to your account |
| `409` | Conflict (duplicate email, expired parent, over-allocation, etc.) |
| `429` | Rate limit |
| `500` | Internal server error — please retry |

---

## 5. FAQ

**Can my sub-users use this API?** No. Sub-users have a dashboard login and proxy credentials, but no API key. Only resellers can manage sub-users.

**Can I have multiple keys?** Yes. Useful for separating environments (e.g. one key per backend service). Each key has its own rate-limit bucket.

**What if I lose a key?** Revoke it from the dashboard and create a new one. We do not store the raw key.

**Is there an SDK?** Not yet. The API is small enough that any HTTP client (curl, requests, axios, fetch) is easy.

**How do I keep my dashboard's sub-user list and my code in sync?** Both views read from the same database — any change you make via API shows up in the dashboard immediately, and vice versa.

**Versioning.** This is `v1`. We will not make breaking changes to `v1` without 30 days notice + parallel `v2`.

---

## 6. LinkedIn Scraper API

A metered LinkedIn scraper (company and person profiles), billed **per request** — a balance separate from your proxy GB. Same API key and rate limit as the reseller API.

- **Base URL:** `https://api.globedata.io/api/v1/scraper`
- **Auth:** identical to §1 (`Authorization: Bearer gd_…`)
- **Entitlement:** the account must have **scraper access enabled** (set by GlobeData). Without it, every scraper endpoint returns `403`.
- **API keys:** scraper users generate their own `gd_` keys from the **API Keys** panel on the dashboard's Scraper page (resellers can also use their existing reseller keys). Same `Authorization: Bearer gd_…` header and 60 req/min/key limit.

### Billing — 1 request per scrape

Every scrape costs **1 request**, whether it's a company or a person. Your balance is measured in requests.

| Profile type | Endpoint | Cost |
|---|---|---|
| Company | `POST /scrape/company` | 1 request |
| Person | `POST /scrape/person` | 1 request |

**What gets charged:** you are charged for a completed lookup — HTTP `200` (found), and also `400`/`404` (the upstream bills these: a `404` is a valid lookup for a profile that doesn't exist). You are **not** charged when the request could not be completed (rate-limit/`429`, upstream `5xx`, or a timeout); those are retried automatically and refunded if they still fail.

> Response fields are named `requests_*` (with legacy `credits_*` aliases kept for backward compatibility — 1 credit = 1 request).

### 6.1 GET `/credits` — your request balance

```bash
curl -H "Authorization: Bearer $GD_KEY" \
  https://api.globedata.io/api/v1/scraper/credits
```

```json
{
  "success": true,
  "requests_total": 2000,
  "requests_used": 340,
  "requests_remaining": 1660,
  "cost_per_request": 1,
  "credits_total": 2000,
  "credits_used": 340,
  "credits_remaining": 1660
}
```

### 6.2 POST `/scrape/company` — company profile

```bash
curl -X POST -H "Authorization: Bearer $GD_KEY" -H "Content-Type: application/json" \
  -d '{"id":"amazon"}' \
  https://api.globedata.io/api/v1/scraper/scrape/company
```

Body: `{ "id": "<linkedin slug>" }`. `id` is the URL slug, e.g. `amazon` from `linkedin.com/company/amazon` (a full LinkedIn URL is also accepted).

```json
{
  "success": true,
  "profile_type": "company",
  "id": "amazon",
  "requests_charged": 1,
  "requests_remaining": 1659,
  "credits_charged": 1,
  "credits_remaining": 1659,
  "data": { "...": "LinkedIn profile data" }
}
```

### 6.3 POST `/scrape/person` — person profile

```bash
curl -X POST -H "Authorization: Bearer $GD_KEY" -H "Content-Type: application/json" \
  -d '{"id":"rbranson"}' \
  https://api.globedata.io/api/v1/scraper/scrape/person
```

Body: `{ "id": "<linkedin id>" }`. `id` is the profile id, e.g. `rbranson` from `linkedin.com/in/rbranson` (a full LinkedIn URL is also accepted).

### 6.4 GET `/history?days=` — daily usage aggregate

```bash
curl -H "Authorization: Bearer $GD_KEY" \
  "https://api.globedata.io/api/v1/scraper/history?days=30"
```

Returns per-day, per-type request counts, success/failure, and requests spent.

### 6.5 Allocating requests to sub-users

Distribute your requests to sub-users, mirroring GB allocation. `credits_delta` is a whole number — positive tops up (bounded by your free balance), negative reclaims (bounded by the sub-user's unused balance).

```bash
# List sub-user balances + your free pool
curl -H "Authorization: Bearer $GD_KEY" \
  https://api.globedata.io/api/v1/scraper/subusers

# Give a sub-user 500 requests
curl -X POST -H "Authorization: Bearer $GD_KEY" -H "Content-Type: application/json" \
  -d '{"credits_delta":500}' \
  https://api.globedata.io/api/v1/scraper/subusers/<subuser-uuid>/allocate-credits
```

### Scraper errors

| Status | Meaning |
|---|---|
| `402 Payment Required` | Insufficient requests (response includes `required` and `available`) |
| `403 Forbidden` | Scraper access not enabled on the account, or sub-user allocation attempted by a non-reseller |
| `400`/`404` | LinkedIn lookup failed but **was charged** (the upstream bills these); body includes `upstream_status` and `charged: true` |
| `429 Too Many Requests` | Scraper at capacity after automatic retries — no request charged; retry shortly |
| `503 Service Unavailable` | Upstream unreachable (no request charged), or scraper not configured on the server |
