InsightSocial API

Testing your integration

Test against the live API without surprises. What is free, what is billed, and which paths to check before launch.

Begin with a call that costs nothing. It proves your key works and records your starting balance before any billed test.

cURL
curl --include "https://api.insightsocial.app/v1/credits" \
  -H "x-api-key: $INSIGHTSOCIAL_API_KEY"
Response
{
  "success": true,
  "credits_used": 0,
  "credits_remaining": 500,
  "plan": { "tier": "free", "credits_per_month": 500, "resets_at": "2026-10-01T00:00:00.000Z" },
  "usage": { "window_start": "2026-09-01T00:00:00.000Z", "used": 0, "used_export": 0, "used_api": 0 },
  "pack_credits": 0,
  "free_calls": { "remaining": 10, "total": 10 },
  "key": { "id": "3f6c2a8e-7b41-4d0f-9a52-1c8e5d7b9f20", "name": "staging" },
  "request_id": "req_1a2b3c4d5e6f"
}

Check for status 200, success: true and credits_used: 0. Keep the request_id in your test log.

There is no sandbox

Every test runs against the real API, with the same prices, caching and rate limits as production.

Keys come in two forms, isk_live_… and isk_test_…. They behave identically: an isk_test_ key reaches the same data and spends real credits from the same balance. The prefix is a label for your own bookkeeping, not a test mode.

Use fixtures for parser tests that must be deterministic, and a small number of budgeted live calls to prove the end-to-end path works today. Neither proves that a source will be available tomorrow.

Use a separate test key

Create a key just for development or staging at Dashboard → API keys and keep it apart from production. Store it as INSIGHTSOCIAL_API_KEY in a server-side secret. Keep it out of source control, browser code, snapshots and logs.

A separate key lets you revoke it without touching production, and it has its own rate limits. It does not have its own budget: keys cannot be capped, and every key spends from the account balance. Record the balance before and after each test run, and revoke the key if it ever shows up in test output. See Authentication.

Free checks first

These cost nothing:

CheckWhat it proves
GET /v1/endpointsThe catalogue, prices and parameters. Needs no key, so it does not test yours.
GET /v1/creditsYour key is valid. Returns balance, plan and free calls left.
dry_run=1 on endpoints that support itReturns a cost estimate in data.estimate without running the call.
Any call that returns an errorErrors are never charged.

Then make one real call to check auth, transport and your parser against the live service:

cURL
curl --include "https://api.insightsocial.app/v1/instagram/profile?handle=natgeo" \
  -H "x-api-key: $INSIGHTSOCIAL_API_KEY"

This endpoint costs 20 credits. On a new account it may use one of your 10 lifetime free calls instead (free_call: true, credits_used: 0). Read credits_used and charge_reason rather than assuming what was billed, and keep live calls out of unit tests and pull-request loops.

Try calls in the Explorer

The Explorer in your dashboard runs any endpoint from the browser, signed in as you, with no key to paste. Use it to see real responses and prices before you write code. Its calls are real and are charged exactly like API calls.

Test fixtures, nulls and dropped rows

Keep small response fixtures in your own test suite, with keys and unneeded personal data removed. This synthetic one covers the states a parser must tell apart:

Success fixture
{
  "success": true,
  "platform": "tiktok",
  "endpoint": "/v1/tiktok/profile/videos",
  "data": {
    "items": [
      {
        "post": {
          "id": "7418806235590471732",
          "url": "https://www.tiktok.com/@khaby.lame/video/7418806235590471732",
          "content": { "text": "Launch day", "media_urls": null, "thumbnail_url": null },
          "author": { "username": "khaby.lame", "display_name": null, "verified": true },
          "engagement": { "views": 1200, "likes": 100, "comments": 12, "shares": null },
          "published_at": "2026-09-01T12:00:00.000Z"
        },
        "computed": { "engagement_rate": null, "language": null }
      }
    ],
    "dropped": 1,
    "_warnings": ["One source row could not be normalized."]
  },
  "pagination": { "next_cursor": null, "has_more": false, "page_size": 1 },
  "credits_used": 20,
  "credits_remaining": 480,
  "request_id": "req_0f1e2d3c4b5a",
  "cached": false,
  "idempotent_replay": false,
  "charge_reason": "miss",
  "free_call": false
}

Assert each case on its own:

  1. A present value, such as post.author.username.
  2. A null, such as computed.language: the field exists but has no value.
  3. An absent optional field. This fixture has no post.ext, so check that a key exists before reading it.
  4. data.dropped: source rows that could not be normalized on this page. Record it per page.
  5. data._warnings: advisory notes. The response is still valid.

See Response schema for the full envelope.

For lists, test a one-page and a multi-page walk: send pagination.next_cursor back unchanged as cursor and stop on has_more: false. See Pagination.

Keep error fixtures too, and branch on error.type:

Error fixture
{
  "success": false,
  "error": {
    "type": "RATE_LIMITED",
    "message": "Too many requests on this key. Honour Retry-After, then back off with jitter."
  },
  "request_id": "req_9a8b7c6d5e4f",
  "credits_used": 0,
  "credits_remaining": 480
}

Add one for every error type you handle: at least INVALID_API_KEY, INSUFFICIENT_CREDITS, RATE_LIMITED, CONCURRENCY_LIMIT and SERVICE_UNAVAILABLE. See Error handling and the retry wrapper in the Production checklist.

Test repeat calls and forced fetches

Send the same call twice:

cURL
curl "https://api.insightsocial.app/v1/instagram/profile?handle=natgeo" \
  -H "x-api-key: $INSIGHTSOCIAL_API_KEY"

curl "https://api.insightsocial.app/v1/instagram/profile?handle=natgeo" \
  -H "x-api-key: $INSIGHTSOCIAL_API_KEY"

The first call may come back as a miss at the full price, or as shared_cache (cached: true) at 5 credits. Either way it counts as paid (so does a call covered by a free call), and the identical second call inside the endpoint's window should come back with charge_reason: "owned" and credits_used: 0. Assert on the charge_reason and credits_used you actually receive. See Caching.

Test a forced fetch only when you need one, and budget it at the endpoint's full price:

cURL
curl "https://api.insightsocial.app/v1/instagram/profile?handle=natgeo" \
  -H "x-api-key: $INSIGHTSOCIAL_API_KEY" \
  -H "Cache-Control: no-cache"

A successful forced fetch is never owned: assert charge_reason: "miss" and the normal charge.

Test retries with an Idempotency-Key

Send the same call twice with one key:

cURL
IDEMPOTENCY_KEY="$(uuidgen)"

curl "https://api.insightsocial.app/v1/instagram/profile?handle=nasa" \
  -H "x-api-key: $INSIGHTSOCIAL_API_KEY" \
  -H "Idempotency-Key: $IDEMPOTENCY_KEY"

curl "https://api.insightsocial.app/v1/instagram/profile?handle=nasa" \
  -H "x-api-key: $INSIGHTSOCIAL_API_KEY" \
  -H "Idempotency-Key: $IDEMPOTENCY_KEY"

The second response should have idempotent_replay: true, charge_reason: "replay" and credits_used: 0. If the second request arrives while the first is still running, expect 409 IDEMPOTENCY_IN_PROGRESS; wait for Retry-After and send it again. Use a new key for every distinct call. See Production checklist.

Pre-production checklist

  • The test key is separate from production, and the balance is recorded before and after each run.
  • The suite lists each endpoint, its current price, and the most calls it will make.
  • Free checks (/v1/credits, /v1/endpoints, dry_run=1) run before any billed call.
  • Success and error fixtures cover values, null, absent fields, dropped rows and warnings.
  • Pagination tests stop on has_more: false and return the cursor unchanged.
  • Retry tests branch on error.type, use bounded delays, and resend the same cursor.
  • Repeat-call tests check charge_reason, cached and credits_used.
  • Forced fetches are budgeted as billed calls.
  • Idempotency tests replay one identical call and use a new key for anything else.
  • Logs tie together status, request_id, error.type, charge reason, credits and attempt count.
  • Production keys never enter the test environment or its output.

Next steps

On this page