GET https://localcitationpro.com/api/v1/me/
Who am I, and what is my balance?
The agency behind the key, its credit balance and its discount.
Example request
curl https://localcitationpro.com/api/v1/me/ -H "Authorization: Bearer $LCP_API_KEY"
Example response
{
"agency": {
"name": "Local Map Booster",
"email": "[email protected]",
"status": "approved"
},
"balanceCents": 52500,
"balanceUsd": 525,
"currency": "usd",
"discountPercent": 0,
"key": {
"prefix": "lcp_live_Ab3dEf",
"label": "Claude"
}
}
GET https://localcitationpro.com/api/v1/packages/
What can I order, and what does it cost me?
Every package orderable from credit, grouped by service, priced after your discount, with the extras each accepts and the country capacity table for citation orders.
- priceUsd is what your credit is charged. listPriceUsd is the website price.
- requiresCitationCountry: true means the order needs citationCountry (2-letter ISO code).
- citationCountries lists every country with maxListingsPerBusiness — a citation order whose sites exceed it is refused.
Example request
curl https://localcitationpro.com/api/v1/packages/ -H "Authorization: Bearer $LCP_API_KEY"
Example response
{
"discountPercent": 0,
"currency": "usd",
"families": [
{
"key": "citation",
"title": "Citation building",
"packages": [
{
"code": "CITE-02",
"name": "Growth",
"tag": "50 citation sites",
"description": "50 local directories — your choice or mine.",
"listPriceUsd": 25,
"priceUsd": 25,
"deliveryDays": 3,
"fastDeliveryUsd": 10,
"citationSites": 50,
"requiresCitationCountry": true,
"extras": [
{
"id": "add-100",
"name": "Add 100 more sites",
"description": "…",
"listPriceUsd": 35,
"priceUsd": 35,
"extraDays": 5,
"citations": 100
}
]
}
]
}
],
"citationCountries": [
{
"code": "US",
"name": "United States",
"maxListingsPerBusiness": 1000
},
{
"code": "IE",
"name": "Ireland",
"maxListingsPerBusiness": 100
}
]
}
POST https://localcitationpro.com/api/v1/quote/
How much would this order cost?
Prices and checks an order without charging anything. Same body as placing an order.
Example request
curl -X POST https://localcitationpro.com/api/v1/quote/ -H "Authorization: Bearer $LCP_API_KEY" -H "Content-Type: application/json" -d '{"planCode":"CITE-02","citationCountry":"US","business":{"businessName":"Summit Plumbing Co.","website":"https://summitplumbing.com"}}'
Request body (JSON)
{
"planCode": "CITE-02",
"citationCountry": "US",
"fastDelivery": false,
"addons": [],
"business": {
"businessName": "Summit Plumbing Co.",
"phone": "+1 (555) 012-3456",
"address": "123 Main St, Austin, TX",
"zip": "78701",
"website": "https://summitplumbing.com",
"gbp": "https://maps.app.goo.gl/example",
"category": "Plumber",
"email": "[email protected]",
"hours": "Mon–Fri 9am–6pm",
"description": "Family-run plumbing company serving Austin since 2014."
}
}
Example response
{
"quote": {
"package": {
"code": "CITE-02",
"name": "Growth",
"tag": "50 citation sites"
},
"quantity": 1,
"extras": [],
"fastDelivery": false,
"citationCountry": "US",
"listCents": 2500,
"discountCents": 0,
"totalCents": 2500,
"currency": "usd",
"deliveryDays": 3,
"balanceCents": 52500,
"canAfford": true
}
}
POST https://localcitationpro.com/api/v1/orders/
Place one order
Charges your credit and creates the order in one step. Answers 201 with the order number.
Idempotency-Key- Optional but recommended: any unique string (a UUID is ideal). If your request times out and you send it again with the same key, you get the same order back — never a second one. Keys are remembered for 24 hours. Only placed orders are remembered: after an error (e.g. insufficient credit) you can retry with the same key once you have fixed the cause.
- planCode is required. citationCountry is required for citation packages. business.businessName and either business.website or business.gbp are required.
- business fields: businessName, phone, address, zip, website, gbp, category, email, hours, since, payments, keywords, description, socials, assets. Everything except name and a link can be added later on the order page.
- addons is a list of extra ids from /packages/ (e.g. ["add-100"]). fastDelivery: true adds 1-day delivery where sold.
- quantity (default 1) is the number of locations for multi-location businesses; most orders leave it out.
- Errors: 400 invalid_order (message says what is wrong), 402/400 insufficient_credit, 409 request_in_progress, 422 idempotency_mismatch.
Example request
curl -X POST https://localcitationpro.com/api/v1/orders/ -H "Authorization: Bearer $LCP_API_KEY" -H "Content-Type: application/json" -H "Idempotency-Key: 4f1c2a9e-1b7d-4c8e-9a1f-2d3e4f5a6b7c" -d '{"planCode":"CITE-02","citationCountry":"US","fastDelivery":false,"addons":[],"business":{"businessName":"Summit Plumbing Co.","phone":"+1 (555) 012-3456","address":"123 Main St, Austin, TX","zip":"78701","website":"https://summitplumbing.com","gbp":"https://maps.app.goo.gl/example","category":"Plumber","email":"[email protected]","hours":"Mon–Fri 9am–6pm","description":"Family-run plumbing company serving Austin since 2014."}}'
Request body (JSON)
{
"planCode": "CITE-02",
"citationCountry": "US",
"fastDelivery": false,
"addons": [],
"business": {
"businessName": "Summit Plumbing Co.",
"phone": "+1 (555) 012-3456",
"address": "123 Main St, Austin, TX",
"zip": "78701",
"website": "https://summitplumbing.com",
"gbp": "https://maps.app.goo.gl/example",
"category": "Plumber",
"email": "[email protected]",
"hours": "Mon–Fri 9am–6pm",
"description": "Family-run plumbing company serving Austin since 2014."
}
}
Example response
{
"order": {
"orderNumber": "LCP-2026-0052",
"status": "paid",
"statusLabel": "Queued",
"totalCents": 2500,
"currency": "usd",
"dueDate": "2026-09-24",
"orderPageUrl": "https://localcitationpro.com/order/<token>/",
"url": "https://localcitationpro.com/api/v1/orders/LCP-2026-0052/"
},
"balanceCents": 50000
}
POST https://localcitationpro.com/api/v1/orders/bulk/
Place many orders at once
Up to 500 orders in one call, each the same shape as a single order. Every order is priced first and the whole batch must fit your balance; otherwise nothing is placed (402). Orders are then placed one by one. If one fails, it stops and tells you exactly which were placed (207) so you never re-send those.
Idempotency-Key- Same as for a single order.
Example request
curl -X POST https://localcitationpro.com/api/v1/orders/bulk/ -H "Authorization: Bearer $LCP_API_KEY" -H "Content-Type: application/json" -H "Idempotency-Key: batch-2026-09-21-a" -d @orders.json
Request body (JSON)
{
"orders": [
{
"planCode": "CITE-02",
"citationCountry": "US",
"fastDelivery": false,
"addons": [],
"business": {
"businessName": "Summit Plumbing Co.",
"phone": "+1 (555) 012-3456",
"address": "123 Main St, Austin, TX",
"zip": "78701",
"website": "https://summitplumbing.com",
"gbp": "https://maps.app.goo.gl/example",
"category": "Plumber",
"email": "[email protected]",
"hours": "Mon–Fri 9am–6pm",
"description": "Family-run plumbing company serving Austin since 2014."
}
},
{
"planCode": "CITE-02",
"citationCountry": "US",
"fastDelivery": false,
"addons": [],
"business": {
"businessName": "Riverside Dental",
"website": "https://riversidedental.com",
"address": "9 Oak Ave, Denver, CO",
"phone": "+1 (555) 987-6543"
}
}
]
}
Example response
{
"ok": true,
"placed": [
{
"orderNumber": "LCP-2026-0053",
"status": "paid",
"statusLabel": "Queued",
"totalCents": 2500,
"currency": "usd",
"dueDate": "2026-09-24",
"orderPageUrl": "https://localcitationpro.com/order/<token>/",
"url": "https://localcitationpro.com/api/v1/orders/LCP-2026-0053/"
},
{
"orderNumber": "LCP-2026-0054",
"status": "paid",
"statusLabel": "Queued",
"totalCents": 2500,
"currency": "usd",
"dueDate": "2026-09-24",
"orderPageUrl": "https://localcitationpro.com/order/<token>/",
"url": "https://localcitationpro.com/api/v1/orders/LCP-2026-0054/"
}
],
"placedCount": 2,
"totalCents": 5000,
"balanceCents": 45000
}
GET https://localcitationpro.com/api/v1/orders/
List my orders
Every order on the account, newest first, with status, due date and the report link once delivered.
status- active (queued, building or submitted), paid, in_progress, submitted, delivered, cancelled, refunded
since- Only orders created on or after this date/time, e.g. 2026-09-01 or 2026-09-01T00:00:00Z
limit- Page size, default 100, max 500
offset- Skip this many, for paging
Example request
curl "https://localcitationpro.com/api/v1/orders/?status=delivered&since=2026-09-01" -H "Authorization: Bearer $LCP_API_KEY"
Example response
{
"total": 21,
"offset": 0,
"limit": 100,
"orders": [
{
"orderNumber": "LCP-2026-0052",
"status": "delivered",
"statusLabel": "Delivered",
"package": {
"code": "CITE-02",
"name": "Growth",
"tag": "50 citation sites"
},
"businessName": "Summit Plumbing Co.",
"citationCountry": "US",
"totalCents": 2500,
"currency": "usd",
"createdAt": "2026-09-21T10:15:00Z",
"dueDate": "2026-09-24",
"deliveredAt": "2026-09-23T16:02:00Z",
"reportUrl": "https://localcitationpro.com/order/<token>/report/",
"orderPageUrl": "https://localcitationpro.com/order/<token>/",
"url": "https://localcitationpro.com/api/v1/orders/LCP-2026-0052/"
}
]
}
GET https://localcitationpro.com/api/v1/orders/{orderNumber}/
One order in full
Business details, chosen sites, the timeline, and once delivered the report page, a print-to-PDF link, the Excel sheet and any files. File downloadUrl links are signed with the order's own token and redirect to a short-lived download.
Example request
curl https://localcitationpro.com/api/v1/orders/LCP-2026-0052/ -H "Authorization: Bearer $LCP_API_KEY"
Example response
{
"order": {
"orderNumber": "LCP-2026-0052",
"status": "delivered",
"statusLabel": "Delivered",
"package": {
"code": "CITE-02",
"name": "Growth",
"tag": "50 citation sites"
},
"quantity": 1,
"extras": [],
"business": {
"businessName": "Summit Plumbing Co.",
"website": "https://summitplumbing.com",
"citationCountry": "US"
},
"selectedSites": [],
"timeline": [
{
"at": "2026-09-21T10:15:00Z",
"kind": "payment",
"label": "Paid from agency credit",
"detail": "Thank you — your order is queued and we'll begin shortly."
},
{
"at": "2026-09-23T16:02:00Z",
"kind": "status",
"label": "Delivered",
"detail": null
}
],
"report": {
"viewUrl": "https://localcitationpro.com/order/<token>/report/",
"pdfUrl": "https://localcitationpro.com/order/<token>/report/?print=1",
"excel": {
"name": "Summit Plumbing Co. — citations.xlsx",
"href": "https://drive.google.com/uc?export=download&id=…"
},
"files": [
{
"id": "…",
"title": "Screenshots.zip",
"kind": "file",
"sizeBytes": 1048576,
"downloadUrl": "https://localcitationpro.com/api/report-files/<id>/?token=…"
}
]
}
}
}
GET https://localcitationpro.com/api/v1/ledger/
Credit history
Every credit movement, newest first: top-ups, order charges, refunds, adjustments. They add up to balanceCents.
Example request
curl https://localcitationpro.com/api/v1/ledger/ -H "Authorization: Bearer $LCP_API_KEY"
Example response
{
"balanceCents": 50000,
"currency": "usd",
"entries": [
{
"id": "…",
"kind": "order_charge",
"label": "Order",
"amountCents": -2500,
"orderNumber": "LCP-2026-0052",
"note": "LCP-2026-0052",
"createdAt": "2026-09-21T10:15:00Z"
},
{
"id": "…",
"kind": "topup",
"label": "Credit added",
"amountCents": 52500,
"orderNumber": null,
"note": "Card top-up",
"createdAt": "2026-09-20T09:00:00Z"
}
]
}
GET https://localcitationpro.com/api/v1/webhook/
Webhook: be told when an order is delivered
Instead of polling, give us an HTTPS URL of yours and we POST a signed JSON message to it the moment one of your orders is delivered. GET shows the current URL and the last delivery result. PUT { url } sets or replaces it and answers with a signing secret (shown once). DELETE removes it. POST sends a test message now.
- The URL must be https:// on a public host. One URL per agency; setting a new one mints a new secret.
- Events: order.delivered (real) and test (from the Send test button or POST). Each message is sent once per order; we retry 3 times if your server does not answer 2xx.
- Headers on every message: X-LCP-Event, X-LCP-Timestamp (unix seconds) and X-LCP-Signature: v1=<hex>. Verify it: HMAC-SHA256 with your secret over the string "<timestamp>.<raw body>" must equal <hex>. Reject messages older than a few minutes.
- The body has id, event, createdAt and data. data carries orderNumber, businessName, package, deliveredAt, reportUrl and url (the API link for the full order).
Example request
curl -X PUT https://localcitationpro.com/api/v1/webhook/ -H "Authorization: Bearer $LCP_API_KEY" -H "Content-Type: application/json" -d '{"url":"https://example.com/hooks/localcitationpro"}'
Request body (JSON)
{
"url": "https://example.com/hooks/localcitationpro"
}
Example response
{
"webhook": {
"agency_id": "…",
"url": "https://example.com/hooks/localcitationpro",
"active": true,
"last_attempt_at": null,
"last_status": null,
"last_error": null,
"created_at": "2026-09-21T18:00:00Z"
},
"secret": "whsec_…",
"note": "Store the secret now; it is not shown again."
}
What a webhook message looks like
POSTed to your URL when an order is delivered. Answer with any 2xx status within 10 seconds. Verify the signature before trusting it.
POST https://example.com/hooks/localcitationpro
Content-Type: application/json
X-LCP-Event: order.delivered
X-LCP-Timestamp: 1790000000
X-LCP-Signature: v1=<hex of HMAC-SHA256(secret, "1790000000." + raw body)>
{
"id": "evt_a1B2c3D4e5F6g7H8",
"event": "order.delivered",
"createdAt": "2026-09-23T16:02:00Z",
"data": {
"orderNumber": "LCP-2026-0052",
"status": "delivered",
"statusLabel": "Delivered",
"businessName": "Summit Plumbing Co.",
"citationCountry": "US",
"package": {
"code": "CITE-02",
"name": "Growth",
"tag": "50 citation sites"
},
"deliveredAt": "2026-09-23T16:02:00Z",
"reportUrl": "https://localcitationpro.com/order/<token>/report/",
"orderPageUrl": "https://localcitationpro.com/order/<token>/",
"url": "https://localcitationpro.com/api/v1/orders/LCP-2026-0052/"
}
}
Verifying in Node.js
const crypto = require("crypto");
const [ts, sig] = [req.headers["x-lcp-timestamp"], req.headers["x-lcp-signature"].replace("v1=", "")];
const expected = crypto.createHmac("sha256", process.env.LCP_WEBHOOK_SECRET).update(ts + "." + rawBody).digest("hex");
const valid = crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected)) && Date.now() / 1000 - Number(ts) < 300;