Quickstart
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.
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. |
| 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. Managed 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.
| 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.
REST API quickstart
Seven curl calls from nothing to citable records. The first four use a cookie jar because account and compliance actions need an interactive owner; the rest use an API key.
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"
}'
Passwords must be at least 12 characters. The response returns your tenant, user and
plan, and sets the session cookie in cookies.txt.
2. Accept the acceptable-use terms
curl -X POST https://api.powerscraping.app/v1/legal/accept \
-H "Content-Type: application/json" -b cookies.txt \
-d '{"document": "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.
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, "include_comments": true}
}'
Job kinds: collect (bounded recent posts), complete_history
(Agency only), enrich_only (re-enrich the last release) and
export (repackage a release). Re-sending the same
Idempotency-Key returns the original job instead of starting a second one.
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 pending, running, succeeded,
failed and cancelled. A job that ends with
error_code: "access_blocked" met a challenge and stopped — that is the
engine behaving correctly, 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"
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": "ChIJ7wV0mHrBhEcR8i0s"},
"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 release also ships a provenance report (what was collected, when,
under which authorization basis) and an exception report (what was
refused, skipped or discarded, and why). Export both with an export job.
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. Enable overage or upgrade the 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. |
| 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.
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. |
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.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. |
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. |
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. |
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/billing/usage |
Any | Metered position and estimated overage. |
POST /v1/billing/checkout |
Session (owner) | Open Stripe Checkout for a paid plan. |
POST /v1/billing/portal |
Session (owner) | Open the Stripe billing portal. |
POST /mcp |
OAuth bearer | The MCP connector endpoint. |