API
SharePush REST API — Reference
Create, schedule, update and track push campaigns from your own backend: authentication, endpoints, request fields, responses, errors and rate limits.
REST API Reference
The SharePush REST API lets your backend do what the dashboard does: create a campaign, schedule it, send live updates and read its results. Every example below uses placeholders. Replace them with your own values.
| Placeholder | What it is | Where to find it |
|---|---|---|
<YOUR_API_TOKEN> |
Your secret API token | Profile menu → API Tokens → Create New Token. It is shown once, copy it right away. See API Tokens & Permissions. |
<SITE_UUID> |
The site's UUID, used to create campaigns | Sites → open the site. The UUID is under the domain name. |
<SITE_TOKEN> |
The site's public token, used to read stats | The apiKey value in your SDK snippet (Settings → Installation). |
<CAMPAIGN_ULID> |
The ID of one campaign | Returned as campaign_ulid when you create the campaign. |
#Basics
- Base URL:
https://api.sharepush.io/api/v1 - Format: send and receive JSON. Always set
Accept: application/json, so errors come back as JSON too. - Authentication: send your token in every request as a bearer token.
1Authorization: Bearer <YOUR_API_TOKEN>
Keep the token server-side. It can create and send notifications to your audience. Never put it in browser JavaScript, a mobile app or a public repository.
#Endpoints
| Method | Path | What it does | Token permission |
|---|---|---|---|
GET |
/ping |
Checks that your token works | read |
POST |
/create/campaign |
Creates a campaign (or updates one when you pass ulid) |
create (update when passing ulid) |
POST |
/campaigns/<CAMPAIGN_ULID>/dispatch |
Sends a new update for a live campaign | update |
POST |
/campaigns/<CAMPAIGN_ULID>/complete |
Closes a live campaign | update |
GET |
/campaigns/stats |
Reads delivery and click counts for a campaign | read |
#Token permissions
A token has one or more permissions: read, create, update, delete. New tokens get read only, so add create and update if you want to send campaigns. A token can also be limited to some of your sites. In that case, calls about any other site return 403. Full details in API Tokens & Permissions.
#1. Check your token
GET /ping
Run this first. It changes nothing and is safe to call anytime.
1curl -X GET "https://api.sharepush.io/api/v1/ping" \
2 -H "Authorization: Bearer <YOUR_API_TOKEN>" \
3 -H "Accept: application/json"
Response 200
1{
2 "status": "success",
3 "message": "Your API Token is valid!",
4 "timestamp": "2026-10-09T10:15:00+00:00"
5}
#2. Create a campaign
POST /create/campaign
Creates a campaign on one of your sites, then sends it now, schedules it, or saves it as a draft.
#Request fields
| Field | Type | Required | Notes |
|---|---|---|---|
uuid |
string | yes | The site's UUID (<SITE_UUID>). |
name |
string | yes | Internal name, shown only in your dashboard. Max 255 characters. |
title |
string | yes | Notification title. Max 65 characters. |
body |
string | yes | Notification text. Max 160 characters. |
action_url |
URL | yes | Where a click takes the subscriber. |
send_type |
string | yes | immediate (send now), schedule (send at scheduled_at) or draft (save only). |
scheduled_at |
date | when send_type is schedule |
Must be in the future. Use ISO 8601 with a timezone offset, e.g. 2026-10-10T09:00:00+02:00. |
type |
string | no | standard (default) or live. A live campaign can receive later updates via dispatch. |
image_url |
URL | no | Large image shown in the notification, where the browser supports it. |
icon_url |
URL | no | Small icon next to the notification. |
bounce_url |
string | no | A page on your site, running the SharePush SDK, that every click passes through before reaching action_url. A path (/go) or a full URL. See Bounce URL. |
prefill |
boolean | no | When true, adds utm_source=sharepush&utm_medium=push&utm_campaign=<title-slug> to action_url. |
renotify |
boolean | no | Live campaigns only: alert the subscriber again when a newer update replaces the previous one. |
filters |
object | no | Sends only to matching subscribers. See Targeting filters. |
ulid |
string | no | Pass a campaign's ULID to update it instead of creating a new one. See Update a campaign. |
#Example
1curl -X POST "https://api.sharepush.io/api/v1/create/campaign" \
2 -H "Authorization: Bearer <YOUR_API_TOKEN>" \
3 -H "Accept: application/json" \
4 -H "Content-Type: application/json" \
5 -d '{
6 "uuid": "<SITE_UUID>",
7 "type": "standard",
8 "name": "Promo Friday",
9 "title": "New Drop Is Live",
10 "body": "Tap to discover today'\''s best offer.",
11 "action_url": "https://example.com/offers",
12 "send_type": "immediate",
13 "prefill": true,
14 "filters": {
15 "browsers": ["Chrome", "Safari"],
16 "os": ["iOS", "Android"],
17 "countries": ["US", "IT"]
18 }
19 }'
The same request in JavaScript (Node 18+) and PHP:
1const response = await fetch("https://api.sharepush.io/api/v1/create/campaign", {
2 method: "POST",
3 headers: {
4 Authorization: "Bearer <YOUR_API_TOKEN>",
5 Accept: "application/json",
6 "Content-Type": "application/json",
7 },
8 body: JSON.stringify({
9 uuid: "<SITE_UUID>",
10 name: "Promo Friday",
11 title: "New Drop Is Live",
12 body: "Tap to discover today's best offer.",
13 action_url: "https://example.com/offers",
14 send_type: "immediate",
15 }),
16});
17
18const result = await response.json();
19console.log(result.campaign_ulid); // keep it for updates, live dispatches and stats
1<?php
2
3$ch = curl_init("https://api.sharepush.io/api/v1/create/campaign");
4curl_setopt_array($ch, [
5 CURLOPT_POST => true,
6 CURLOPT_RETURNTRANSFER => true,
7 CURLOPT_HTTPHEADER => [
8 "Authorization: Bearer <YOUR_API_TOKEN>",
9 "Accept: application/json",
10 "Content-Type: application/json",
11 ],
12 CURLOPT_POSTFIELDS => json_encode([
13 "uuid" => "<SITE_UUID>",
14 "name" => "Promo Friday",
15 "title" => "New Drop Is Live",
16 "body" => "Tap to discover today's best offer.",
17 "action_url" => "https://example.com/offers",
18 "send_type" => "immediate",
19 ]),
20]);
21
22$result = json_decode(curl_exec($ch), true);
23curl_close($ch);
24
25echo $result["campaign_ulid"];
Response 201 (200 when updating)
1{
2 "success": true,
3 "message": "Campaign created and broadcast started.",
4 "campaign_id": 1234,
5 "campaign_ulid": "01JABCDEF0123456789XYZABCD",
6 "final_url": "https://example.com/offers?utm_source=sharepush&utm_medium=push&utm_campaign=new-drop-is-live"
7}
Save campaign_ulid: you need it to update the campaign, send live updates and complete it. final_url is the click URL after prefill was applied.
#Schedule for later
Set send_type to schedule and add scheduled_at:
1{
2 "uuid": "<SITE_UUID>",
3 "name": "Weekend Sale",
4 "title": "Weekend Sale Starts Now",
5 "body": "Up to 40% off until Sunday night.",
6 "action_url": "https://example.com/sale",
7 "send_type": "schedule",
8 "scheduled_at": "2026-10-10T09:00:00+02:00"
9}
#Update a campaign
Send the same request with the campaign's ulid added. The token needs the update permission, and uuid must be the site the campaign belongs to.
1{
2 "ulid": "<CAMPAIGN_ULID>",
3 "uuid": "<SITE_UUID>",
4 "name": "Promo Friday Updated",
5 "title": "Offer Extended",
6 "body": "Final hours: claim your discount now.",
7 "action_url": "https://example.com/offers/final-hours",
8 "send_type": "draft"
9}
Every update follows its
send_type. Withimmediatethe notification is sent again to your audience. Usedraftto change a campaign without sending it, andscheduleto move it to a new time.
#Targeting filters
filters limits who receives the notification. Every key is optional. An empty or missing list means "everyone".
| Key | Example values |
|---|---|
browsers |
["Chrome", "Safari", "Firefox", "Edge"] |
os |
["Android", "iOS", "Windows", "macOS"] |
countries |
Two-letter country codes: ["US", "IT", "DE"] |
Use the same names that appear in your dashboard reports. Filters apply to every send type and to live dispatches.
#3. Send a live update
POST /campaigns/<CAMPAIGN_ULID>/dispatch
Live campaigns ("type": "live") are made for news that keeps developing, such as a match, an election or a breaking story. Each dispatch sends a new notification that replaces the previous one on the subscriber's device instead of stacking up.
| Field | Type | Required | Notes |
|---|---|---|---|
title |
string | yes | Max 65 characters. |
body |
string | yes | Max 160 characters. |
image_url |
URL | no | |
renotify |
boolean | no | true makes the device alert again (sound or vibration) for this update. |
filters |
object | no | Same as Targeting filters. |
1curl -X POST "https://api.sharepush.io/api/v1/campaigns/<CAMPAIGN_ULID>/dispatch" \
2 -H "Authorization: Bearer <YOUR_API_TOKEN>" \
3 -H "Accept: application/json" \
4 -H "Content-Type: application/json" \
5 -d '{
6 "title": "Live Update",
7 "body": "Second half underway: 1-0.",
8 "renotify": true
9 }'
Response 200
1{
2 "success": true,
3 "message": "Live update for the campaign dispatched successfully.",
4 "campaign": "<CAMPAIGN_ULID>"
5}
#Limit: 5 sends per live campaign
A live campaign can send 5 notifications in total, counting the first one sent when it was created. Once all 5 have been sent, the next dispatch call closes the campaign instead of sending, and still returns 200:
1{
2 "success": false,
3 "cap_reached": true,
4 "message": "Max Live Dispatches Reached, Campaign Completed. Max live updates: 5.",
5 "campaign": "<CAMPAIGN_ULID>",
6 "campaign_status": "completed"
7}
Check the success field, not only the HTTP status. To keep going, create a new live campaign.
Calling dispatch on a standard campaign returns 400.
#4. Complete a live campaign
POST /campaigns/<CAMPAIGN_ULID>/complete
Closes a live campaign when the story is over. No body needed.
1curl -X POST "https://api.sharepush.io/api/v1/campaigns/<CAMPAIGN_ULID>/complete" \
2 -H "Authorization: Bearer <YOUR_API_TOKEN>" \
3 -H "Accept: application/json"
Response 200
1{
2 "success": true,
3 "message": "Campaign marked as completed successfully.",
4 "campaign": "<CAMPAIGN_ULID>"
5}
#5. Read campaign stats
GET /campaigns/stats?site_token=<SITE_TOKEN>&title=<TITLE>
Finds a campaign by its title on a site and returns its counters. It's built for CMS integrations that know an article's title but not the campaign's ID.
| Query parameter | Required | Notes |
|---|---|---|
site_token |
yes | The site's public token (<SITE_TOKEN>), not the UUID. |
title |
yes | The campaign title, matched exactly. |
1curl -G "https://api.sharepush.io/api/v1/campaigns/stats" \
2 -H "Authorization: Bearer <YOUR_API_TOKEN>" \
3 -H "Accept: application/json" \
4 --data-urlencode "site_token=<SITE_TOKEN>" \
5 --data-urlencode "title=New Drop Is Live"
Response 200
1{
2 "data": {
3 "img": "https://example.com/images/drop.jpg",
4 "title": "New Drop Is Live",
5 "description": "Tap to discover today's best offer.",
6 "send_time": "2026-10-09 10:15:00",
7 "status": "sent",
8 "sent": true,
9 "impression": 15230,
10 "success": 15230,
11 "failure": 112,
12 "delivered": 13980,
13 "click": 642,
14 "ctr": "4,22"
15 },
16 "generated_at": "2026-10-09T10:30:00+00:00"
17}
| Field | Meaning |
|---|---|
status |
draft, scheduled, sending, sent or failed. |
sent |
true once the campaign has started sending. |
impression / success |
Notifications accepted by the push services. |
failure |
Notifications the push services rejected. |
delivered |
Notifications that reached the device. |
click |
Clicks on the notification. |
ctr |
click / impression as a percentage, with a comma as the decimal separator ("4,22" = 4.22%). |
Good to know:
- If several campaigns share the title, the most recent one is returned. Only the first 65 characters of the title are compared.
- No match returns
"data": nullwith status200, not an error. - Results are cached for about 25 minutes, so polling every 15 minutes is fine. Polling faster won't give fresher numbers.
#Errors
Errors come back as JSON with an error or message field explaining what went wrong.
| Status | When |
|---|---|
400 |
Dispatch or complete called on a campaign that isn't live. |
401 |
Token missing, invalid or expired, or its owner is no longer a member of your team. |
403 |
The token lacks the needed permission or access to that site, the site isn't yours, or your plan doesn't include API access. |
404 |
Campaign or site not found for your team. |
422 |
A field failed validation. The response lists each field with its error (see below). Also returned when uuid doesn't match the campaign's site on an update. |
429 |
Rate limit reached. The message says how many seconds to wait. |
500 |
Server error. See Retrying safely. |
A validation error looks like this:
1{
2 "message": "The title field must not be greater than 65 characters.",
3 "errors": {
4 "title": ["The title field must not be greater than 65 characters."]
5 }
6}
#Retrying safely
A 500 from create includes a retryable flag:
"error": "Unable to save the campaign, please retry": nothing was saved, so you can safely send the same request again."error": "Campaign saved but delivery could not be started": the campaign exists, and itscampaign_ulidis in the response. Don't create it again, or subscribers may get it twice. Ifretryableistrue, retry as an update with thatulid. If it'sfalse(animmediatesend that may have partly gone out), check the campaign in your dashboard first.
#Rate limits
- Per hour: each team, and each IP address, gets a request allowance set by your plan. Over the limit you get
429with the number of seconds to wait. - Stats:
/campaigns/statsalso allows at most 120 requests per minute. - No API access: if your plan doesn't include the API, every call returns
403. Upgrade from the pricing page to enable it.