Quickstart
One collector, two surfaces, one evidence model
Everything below runs against
https://api.powerscraping.app. Whichever
surface you use, the contract is the same: collection only runs against a declared
authorized target, and every derived field in the result names the evidence it came
from.
Collect with your own Instagram account, in your own browser, with the Power Scraping Collector extension (desktop Chrome, Edge or Brave). We never see your Instagram password. The REST API and the MCP connector start jobs and read results; the collecting itself always happens in the Collector.
The Collector: collecting in your own browser
No Instagram login of ours serves your jobs, and no third-party collection provider is involved. Every post is collected by the Power Scraping Collector browser extension, inside your own browser, under your own Instagram login, from your own device and network. Your Instagram password and session never leave your browser: we never receive, store or replay them. The extension has no cookies permission and never reads cookies. It has no standing access to instagram.com either: in manual mode it reads only the tab you click it on, and automatic mode needs a permission to open instagram.com, which the Collector asks for when you allow automatic collection and gives up whenever automatic collection is off.
1. Install
Use desktop Chrome, Edge or Brave. During the pilot the Collector is installed from a download rather than from a store:
- Download power-scraping-collector.zip (also linked from the dashboard's Collector panel) and unzip it into a folder you will keep.
-
Open the browser's extensions page:
chrome://extensions,edge://extensionsorbrave://extensions. - Turn on Developer mode.
- Choose Load unpacked and select the unzipped folder.
A Chrome Web Store listing is coming. Phones, Safari and Firefox cannot collect; any browser can still read, search and download results.
2. Pair it with your account
In the dashboard's Collector panel, create a pairing code. It is short, single-use and expires after 10 minutes. Paste it into the Collector (its icon in the browser toolbar), check that it names your organisation, and confirm. The paired Collector works for the person who created the code: it collects the jobs that person submits, including jobs submitted with an API key that person created.
To stop using a browser, press Disconnect this browser in the Collector or Disconnect in the dashboard; either revokes it at once. We delete a disconnected browser's record - the name you gave it, the Instagram handle it reported and its timestamps - 30 days after it is disconnected, and the Instagram handle recorded against each job 30 days after the job finishes.
3. Collect: manual or automatic
| Mode | What happens | Account risk |
|---|---|---|
| Manual (the default) | You open the target's posts on instagram.com yourself and press Send this post on each one. | No automated activity is added to your account. |
| Automatic (opt-in, per browser) | After you turn it on for that browser in the dashboard, acknowledge the account risk and allow it in the Collector, the Collector opens the target's profile and its posts in a visible tab, at a fixed slow pace, within small daily caps. | Automated activity can lead Instagram to show warnings, apply temporary action blocks or restrict your account. |
Either way, every job needs your click to start. A job waiting for your browser shows Waiting for your browser in the dashboard until you open the Collector and press Start. Once started, a manual job stays with your browser for 4 hours, renewed while the Collector is in use, and an automatic job for 30 minutes. The Collector stops at the first Instagram warning, challenge or logout. A challenge pauses that browser for 24 hours and switches automatic mode off until you turn it back on.
For a target declared as your own account (own_account), the Collector
collects only while the browser is signed in to Instagram as that account - for
Send this post as well as for jobs. A post already sent in the last 24
hours is a duplicate: it is not added again and not metered again.
What it sends, and what it never sends
Only these fields of the declared target's posts: post id, handle, permalink, dates, caption, location label, media type, links to the media on Instagram's servers, and like and comment counts. It never sends comments, other page text, your feed, your messages or who you follow. Posts of any other account are dropped on your device and again on our server. We never download the photos or videos themselves: records keep the links only. We then enrich, meter, store and export those records under your plan's retention rules.
Your account, your responsibility. Automated activity may trigger Instagram warnings, temporary action blocks or restrictions on your Instagram account. You must follow Instagram's terms, and you must have a lawful basis for every profile you collect - one of the four declared authorization bases below.
Authentication
Three credential kinds reach the same API, scoped to the same tenant.
| Credential | How it is presented | Use it for |
|---|---|---|
| Session cookie | psapp_session, set by /v1/auth/login, http-only |
The dashboard in a browser. Never used by server-side code. |
| API key | Authorization: Bearer psa_live_… |
Your own backends, schedulers and scripts. A key acts for the person who created it: the collection jobs it submits run in that person's paired Collector. |
| OAuth access token | Authorization: Bearer with a JWT for audience /mcp |
The MCP connector, issued to an AI client after you consent. |
An API key is shown exactly once, when you create it. Only its prefix and a hash are stored, so a lost key is revoked and replaced, never recovered.
Step 0: declare what you may collect
This is not paperwork you can defer. Collection is refused with
collection_not_authorized until two things are true: your account has
accepted the current acceptable-use version, and the handle is an active authorized
target with a basis and a reference. The Collector also drops, on your device and again
on our server, any post that belongs to another account.
| Basis | What it asserts | Typical reference |
|---|---|---|
own_account |
The profile belongs to your organisation. | Internal account register entry |
creator_contract |
You hold a signed agreement with the creator. | Contract number and clause |
client_mandate |
The account owner instructed you in writing. | MSA or statement of work reference |
public_interest_research |
Documented research in the public interest. | Ethics approval or research protocol id |
Revoke a target the moment its basis stops being true. Revocation takes effect immediately: the next job for that handle is refused. If the owner of a profile objects to its collection, we revoke it for them: that profile is then suppressed across the whole service and cannot be declared again, by you or by any other customer.
REST API quickstart
Eight curl calls from nothing to citable records you can download. The first four use a cookie jar because account and compliance actions need an interactive owner; the rest use an API key, which acts for the person who created it - you, in step 4. Before step 5, pair the Collector in your browser from the dashboard's Collector panel: collection runs there, and a collection job submitted with your key waits for your browser.
1. Create an account
curl -X POST https://api.powerscraping.app/v1/auth/signup \
-H "Content-Type: application/json" \
-c cookies.txt \
-d '{
"email": "you@example.com",
"password": "a-long-passphrase-you-chose",
"organisation_name": "Acme Guides",
"accept_terms": true,
"business_use": true
}'
Passwords must be at least 12 characters. "accept_terms": true records your
agreement to the current
Terms,
Privacy notice and
Acceptable use; without
it the signup is refused with terms_not_accepted.
"business_use": true confirms "I am using Power Scraping for my trade,
business or profession" and records your business-customer declaration; Power Scraping
is sold to business customers only, so the signup is refused without it as well. The
response returns your tenant, user and plan, and sets the session cookie in
cookies.txt.
2. Accept a document when it changes
curl -X POST https://api.powerscraping.app/v1/legal/accept \
-H "Content-Type: application/json" -b cookies.txt \
-d '{"document": "acceptable_use"}'
Signing up records the versions current at the time. When a document is updated,
GET /v1/me lists it under legal with
"is_current": false, and this call records the new version - the
document is terms_of_service, privacy_notice or
acceptable_use. Omit version and the current published
version is recorded. Read it first at
https://api.powerscraping.app/v1/legal/acceptable-use.
This is the one endpoint that insists on a signed-in user: the cookie from step 1, or
the button in the dashboard. An API key or an MCP token is refused with
permission_denied, because accepting the terms is the customer's own act
and nothing running unattended may perform it on their behalf.
3. Declare an authorized target
curl -X POST https://api.powerscraping.app/v1/targets \
-H "Content-Type: application/json" -b cookies.txt \
-d '{
"platform": "instagram",
"username": "trattoriadaoscar",
"authorization_basis": "client_mandate",
"authorization_reference": "MSA-2026-114, schedule 2"
}'
4. Mint an API key
curl -X POST https://api.powerscraping.app/v1/keys \
-H "Content-Type: application/json" -b cookies.txt \
-d '{"name": "ingest worker"}'
{
"id": "key_7c1f…",
"name": "ingest worker",
"prefix": "psa_live_9Qd2",
"active": true,
"api_key": "psa_live_9Qd2………",
"notice": "Copy this key now. It is shown once and cannot be retrieved again."
}
Export it for the calls below: export PSAPP_KEY=psa_live_9Qd2…. Store it in
your secret manager, not in source control.
The key acts for you, the person who created it: every collection job it submits runs in your paired Collector, and waits there for you to press Start. A key with no creator cannot start collection jobs.
5. Submit a collection job
curl -X POST https://api.powerscraping.app/v1/jobs \
-H "Authorization: Bearer $PSAPP_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: oscar-2026-09-17" \
-d '{
"kind": "collect",
"target_username": "trattoriadaoscar",
"platform": "instagram",
"params": {"max_posts": 50}
}'
Job kinds: collect (bounded recent posts) and enrich_only
(re-enrich the last release). Re-sending the same Idempotency-Key returns
the original job instead of starting a second one.
A collection job does not run on our servers. It runs in the
Collector, in the paired browser of the person who submitted
it - for an API key, the person who created the key - and only after that person
presses Start. If that person has no paired browser, the job is refused with
collector_required.
6. Poll the job
curl https://api.powerscraping.app/v1/jobs/job_5f2a \
-H "Authorization: Bearer $PSAPP_KEY"
{
"id": "job_5f2a",
"kind": "collect",
"target_username": "trattoriadaoscar",
"status": "succeeded",
"posts_processed": 48,
"records_written": 48,
"release_id": "rel_2026_09_17_01",
"is_terminal": true,
"results_available": true
}
Statuses are awaiting_browser (waiting for the Collector: open it and
press Start), pending, running, succeeded,
failed and cancelled. A job that stopped at an Instagram
warning, challenge or logout did exactly what it should - the Collector stops there by
design. It is not a bug to retry around.
7. Read the records
curl "https://api.powerscraping.app/v1/jobs/job_5f2a/results?limit=10&offset=0" \
-H "Authorization: Bearer $PSAPP_KEY"
8. Download every record
curl -OJ "https://api.powerscraping.app/v1/jobs/job_5f2a/export?format=csv" \
-H "Authorization: Bearer $PSAPP_KEY"
format=csv or format=jsonl. The whole job comes back in one
file, in order, with its evidence citations - no paging. A job that has not succeeded
answers 409. The dashboard's Download CSV and
Download JSONL buttons fetch the same files. Cells that would start a
spreadsheet formula are neutralised, so a caption cannot become a live formula.
Reading a record and its citations
Each record carries its evidence items and the citations that bind fields to them. A
field with no resolvable citation is not written at all, so a null means
"we could not evidence this", never "we did not get round to it".
{
"record_id": "rec_8d31f0a4",
"source_url": "https://www.instagram.com/p/Cx1y2z3/",
"creator_handle": "trattoriadaoscar",
"venue": {"name": "Trattoria da Oscar", "place_id": null},
"location": {"city": "Milan", "confidence": 0.94},
"assessment": {"sentiment": "positive", "explicit_rating": null},
"price": {"mentions_eur": [14.0, 22.0], "signal": "mid_range"},
"unknown_fields": ["assessment.explicit_rating"],
"citations": [
{
"field": "venue.name",
"evidence_id": "ev_01",
"modality": "caption",
"excerpt": "finally back at @trattoriadaoscar for the cotoletta",
"method": "caption_mention",
"confidence": 0.97
},
{
"field": "price.mentions_eur",
"evidence_id": "ev_03",
"modality": "caption",
"excerpt": "cotoletta 22 euro, calice di rosso 14",
"method": "price_extraction",
"confidence": 0.93
}
]
}
Every job also records what was collected, when, and under which declared
authorization basis, and every field carries the evidence it was derived
from. Download every record of a job, citations included, from
GET /v1/jobs/{job_id}/export?format=csv or
?format=jsonl.
Errors
Every failure you can act on has the same shape and a stable machine-readable code.
{
"error": {
"code": "collection_not_authorized",
"message": "No active authorized target for 'trattoriadaoscar' on instagram.",
"detail": {"platform": "instagram", "username": "trattoriadaoscar"}
}
}
| HTTP | Code | What to do |
|---|---|---|
| 401 | unauthenticated |
Present a valid credential. A revoked key reads the same as a missing one. |
| 402 | quota_exceeded |
The period allowance is spent, so jobs stop. Wait for the next period or move to a larger plan. |
| 403 | collection_not_authorized |
Declare the target, or accept the current acceptable-use version - the latter from a signed-in session, not from a key or token. |
| 403 | permission_denied |
The action needs an account owner, not an API key. |
| 404 | not_found |
No such item in your tenant. Anything belonging to another tenant answers 404 as well, deliberately. |
| 409 | conflict |
Already declared, or the job has already finished and cannot be cancelled. |
| 409 | collector_required |
The person the job would run for - you, or for an API key the person who created it - has no paired Collector. Pair one in the dashboard's Collector panel and submit again. |
| 409 | subscription_exists |
Checkout refused: the account already has a subscription. Change plan in the
billing portal, POST /v1/billing/portal.
|
| 409 |
terms_not_accepted,
business_declaration_required
|
Checkout refused: accept the current Terms, and confirm you are buying for your
trade, business or profession - the business-use declaration you made at sign-up
counts, or send "business_customer": true.
|
| 422 | invalid_request |
Read detail.field; it names the offending field. |
| 429 | rate_limited |
Back off and retry. |
MCP connector quickstart
The connector exposes the same evidence-grounded records to an AI client over the Model
Context Protocol, Streamable HTTP transport, protocol revision
2025-06-18.
Collecting from a chat needs the Collector. Before collection works,
the chat user needs the Power Scraping Collector installed and
paired on a desktop browser (Chrome, Edge or Brave). Until then
start_collection answers
collector_required, with a link to set it up. Reading and searching
records works from any client, phones included.
Add the server
In Claude, ChatGPT or an IDE agent that supports remote MCP servers, add a connector with this URL:
https://api.powerscraping.app/mcp
The client discovers authorization by itself: the server answers an unauthenticated call
with 401 and a WWW-Authenticate header pointing at its RFC 9728
metadata. Registration, consent and token exchange then happen without you pasting a
secret anywhere.
| Purpose | Path |
|---|---|
| Protected-resource metadata (RFC 9728) | /.well-known/oauth-protected-resource |
| Authorization-server metadata (RFC 8414) | /.well-known/oauth-authorization-server |
| Dynamic client registration (RFC 7591) | /oauth/register |
| Authorization (PKCE required) | /oauth/authorize |
| Token | /oauth/token |
| Revocation (RFC 7009) | /oauth/revoke |
The tools
| Tool | What it does |
|---|---|
list_authorized_targets |
The handles this account may collect, with basis and reference. |
authorize_target |
Declare a new target, naming the basis and an auditable reference. |
start_collection |
Submit a collection job for a declared target. Returns a job id, or
collector_required with a setup link when no Collector is paired.
The job then runs in that person's Collector once they click to start it.
|
get_job_status |
Poll the job, including the reason a job stopped. |
get_venue_records |
Read a page of enriched records with their citations. |
search_records |
Filter finished records by city, price band, sentiment or tag. |
get_usage |
The plan, the period allowance and what is left of it. |
A typical conversation
list_authorized_targetsto see what may be collected.authorize_targetif the handle is missing — the basis is required.get_usagebefore a large run, so the job is not refused mid-way.-
start_collection, thenget_job_statusuntil it succeeds. If it answerscollector_required, follow the link, pair the Collector on a desktop browser, and ask again. get_venue_recordsorsearch_recordsto answer the question.
Tell the assistant to quote citations rather than fill gaps. When a field comes back null it is genuinely unknown, and the whole point of this connector is that the model can say so.
Endpoint reference
The full generated specification lives at
https://api.powerscraping.app/api/openapi.json,
with interactive docs at
https://api.powerscraping.app/api/docs.
| Method and path | Credential | Purpose |
|---|---|---|
GET /v1/plans |
None | The published plan catalogue. |
GET /v1/legal/acceptable-use |
None | The current terms and their version. |
POST /v1/auth/signup |
None |
Create a tenant and its owner. Needs "accept_terms": true and
"business_use": true.
|
POST /v1/auth/login |
None | Start a dashboard session. |
POST /v1/legal/accept |
Session only |
Record acceptance of a policy version. An API key or an MCP token is refused
with permission_denied, deliberately: this row is the account's
proof of who agreed to the terms and when, so an unattended agent must not be
able to bind the customer to them.
|
GET /v1/me |
Any |
The account, plan and usage behind the credential, and under
legal whether each accepted document is still current.
|
GET POST /v1/targets |
Session (write), any (read) | List or declare authorized targets. |
DELETE /v1/targets/{id} |
Session | Revoke a declaration. |
GET POST /v1/keys |
Session (owner) | List or create API keys. |
DELETE /v1/keys/{id} |
Session (owner) | Revoke a key. |
POST /v1/collectors/pairing-codes |
Session only | A single-use pairing code for the Collector, valid for 10 minutes, with the organisation name the Collector will show. |
GET /v1/collectors |
Session only | Your paired browsers; an owner sees every browser in the workspace. |
PATCH /v1/collectors/{id} |
Session only |
Turn automatic collection on ("guided_enabled": true with
"accept_account_risk": true, your own browser only) or off.
|
DELETE /v1/collectors/{id} |
Session only | Disconnect a browser. It is refused from its next request on. |
GET POST /v1/jobs |
Any | List or submit jobs. |
POST /v1/jobs/{id}/cancel |
Any | Cancel a job that has not finished. |
GET /v1/jobs/{id}/results |
Any | A page of records with citations. |
GET /v1/jobs/{id}/export?format=csv|jsonl |
Any | Every record of a succeeded job, as one CSV or JSON Lines download. |
GET /v1/billing/usage |
Any | Metered position for the current period. |
POST /v1/billing/checkout |
Session (owner) |
Open Stripe Checkout for a first paid plan. Needs the business-use declaration
made at sign-up, or "business_customer": true.
|
POST /v1/billing/portal |
Session (owner) | Open the Stripe billing portal: change plan, update card, cancel. |
POST /mcp |
OAuth bearer | The MCP connector endpoint. |