ShareSomeone API · v1 · Limited beta
API documentation
Limited access beta. The ShareSomeone API is available only to accounts invited by SOMEONE while it is in beta. It isn't open for sign-up, and credits can't be bought yet. What's described here may change before general availability.
Publish to the networks connected to your SOMEONE account from your own app. Posts made with the API go through the same ShareSomeone publishing as posts made on sharesomeone.com: the same checks, deliveries, retries, scheduling and History.
Quickstart
- Sign in to ShareSomeone with your SOMEONE account and connect your networks on the Accounts page.
- Open Developers, turn on developer access and create an API key. Copy it: it is shown once.
- List your destinations, then create a post. Start with
"mode": "test": the post goes through every check and step but nothing is sent to any network, and it costs no credits.
curl https://sharesomeone.com/v1/destinations \
-H "Authorization: Bearer someone_sk_XXXXXXXXXXXX_your_secret_here"
curl https://sharesomeone.com/v1/posts \
-H "Authorization: Bearer someone_sk_XXXXXXXXXXXX_your_secret_here" \
-H "Idempotency-Key: 6f1c2a9e-first-post" \
-H "Content-Type: application/json" \
-d '{"text": "Hello from my app", "destinations": ["dest_XXXXXXXXXXXXXXXXXXXXXX"], "mode": "test"}'
JavaScript (Node 18+ or any runtime with fetch):
const key = process.env.SHARESOMEONE_API_KEY; // never hard-code a key
const api = (path, init = {}) =>
fetch(`https://sharesomeone.com${path}`, { ...init, headers: { authorization: `Bearer ${key}`, "content-type": "application/json", ...init.headers } })
.then(async (r) => { const body = await r.json(); if (!r.ok) throw Object.assign(new Error(body.error.message), body.error); return body; });
const { data: destinations } = await api("/v1/destinations");
const post = await api("/v1/posts", {
method: "POST",
headers: { "idempotency-key": crypto.randomUUID() },
body: JSON.stringify({ text: "Hello from my app", destinations: destinations.map((d) => d.id), mode: "test" }),
});
console.log(post.id, post.status);
Python (requests):
import os, uuid, requests
API = "https://sharesomeone.com/v1"
headers = {"Authorization": f"Bearer {os.environ['SHARESOMEONE_API_KEY']}"}
destinations = requests.get(f"{API}/destinations", headers=headers).json()["data"]
r = requests.post(
f"{API}/posts",
headers={**headers, "Idempotency-Key": str(uuid.uuid4())},
json={"text": "Hello from my app", "destinations": [d["id"] for d in destinations], "mode": "test"},
)
r.raise_for_status()
post = r.json()
print(post["id"], post["status"])
Authentication
Send your key as a bearer token on every request: Authorization: Bearer someone_sk_…. Keys belong to one SOMEONE account and act only on that account's destinations, posts and media. Use HTTPS only. A missing, unknown, revoked or expired key gets 401; a key without the needed permission gets 403 insufficient_scope.
API keys and permissions
Create, name, replace and revoke keys on the Developers page (never through the API). A key looks like someone_sk_ + a 12-character id + a secret. The full key is shown once; afterwards you see its prefix, creation date and last use. Replacing a key creates a new one with the same name and permissions and revokes the old one immediately.
| Permission | Allows |
|---|---|
destinations:read | GET /v1/destinations |
posts:write | Create posts, preflight, cancel a schedule, retry a failed delivery |
posts:read | GET /v1/posts, GET /v1/posts/{id} |
deliveries:read | GET a post's deliveries |
media:write | Upload photos and videos |
webhooks:read / webhooks:write | See / manage webhook endpoints |
credits:read | GET /v1/credits |
A key has only the permissions it was created with. New permissions added in the future are never granted to existing keys.
Destinations
GET /v1/destinations lists the destinations connected to your account: every account, Page, channel or profile is its own destination with a stable id (dest_…). You publish to destination ids; you never handle a network's own login or tokens. Connect and disconnect networks in ShareSomeone.
{"object": "list", "data": [
{"id": "dest_XXXXXXXXXXXXXXXXXXXXXX", "network": "linkedin", "network_name": "LinkedIn", "display_name": "The Newsroom",
"kind": "page", "status": "active",
"capabilities": {"text_only": true, "images": true, "videos": true, "max_text_length": 3000, "max_text_length_with_media": 3000,
"max_image_bytes": 10485760, "max_video_bytes": 524288000}}
]}
status is active, needs_reconnect (reconnect it in ShareSomeone) or unavailable.
Create a post
POST /v1/posts (requires an Idempotency-Key) creates one ShareSomeone post with one delivery per destination.
| Field | Description |
|---|---|
text | The post text. |
link | Optional; added at the end of the text. |
media | Optional list of media ids ("med_…" or {"id": "med_…", "alt_text": "…"}), up to 10. |
destinations | List of destination ids, or objects {"id": "dest_…", "settings": {…}} for network settings. |
schedule | Optional {"at": "2026-10-05T14:30:00-04:00"}. See Scheduling. |
mode | "live" (default) publishes; "test" runs every step without sending anything (free). |
Network settings, where a network needs your choice:
{"id": "dest_youtube…", "settings": {"youtube": {"title": "My video", "description": "Optional override",
"privacy": "public", "made_for_kids": false, "tags": ["news"], "category_id": "25", "notify_subscribers": true}}}
{"id": "dest_tiktok…", "settings": {"tiktok": {"privacy": "SELF_ONLY", "allow_comment": true, "allow_duet": false,
"allow_stitch": false, "commercial_content": false, "consent_acknowledged": true}}}
YouTube needs a title, a privacy choice and the made-for-kids answer. TikTok needs a privacy choice from the account's allowed options and your acknowledgement of TikTok's posting terms. Use preflight to see exactly what each destination still needs.
One post counts as one post, however many destinations it goes to; each delivery is tracked separately.
Media uploads
Upload a file first, then reference its id. Files are private, owned by your account (another account can never use your media id) and deleted from ShareSomeone after delivery.
curl https://sharesomeone.com/v1/media \
-H "Authorization: Bearer someone_sk_XXXXXXXXXXXX_your_secret_here" \
-F "file=@photo.jpg"
# -> {"id": "med_…", "object": "media", "type": "image", "mime_type": "image/jpeg", "size_bytes": 182340, ...}
Files up to 100 MB can be sent in one request. For larger videos (up to 2 GB), upload in parts:
POST /v1/media/uploads {"file_name": "clip.mp4", "content_type": "video/mp4", "size": 734003200}
-> {"id": "<upload id>", "part_size_bytes": 52428800, "max_parts": 100}
PUT /v1/media/uploads/{id}/parts/1 (raw bytes of part 1) -> {"part_number": 1, "etag": "…"}
...
POST /v1/media/uploads/{id}/complete {"parts": [{"part_number": 1, "etag": "…"}, ...]}
-> {"id": "med_…", "object": "media", "type": "video", ...}
Scheduling
Add "schedule": {"at": "…"} with an ISO 8601 time that includes a time zone offset or Z (a time without one is refused as ambiguous). The time is stored as UTC; the post appears under Scheduled in ShareSomeone like any other scheduled post. Cancel with DELETE /v1/posts/{id}/schedule. Every network is checked again when the post goes out.
Post status
GET /v1/posts/{id} returns the post and its deliveries. GET /v1/posts?limit=20&starting_after=post_… lists the posts you created with the API, newest first.
| Post status | Meaning |
|---|---|
queued | Accepted, about to go out. |
scheduled | Waiting for its scheduled time. |
processing | Going out, or a network is still finishing it. |
published | Every destination published. |
partially_published | Some destinations published; others failed. |
needs_attention | Nothing published and something needs you (e.g. reconnect a network). |
failed | No destination published. |
Deliveries and retries
Each destination is its own delivery (dlv_…) with a status (queued, scheduled, processing, published, failed, needs_attention), the public post url when the network gives one, and an error with a readable message when it failed. One destination failing never stops the others.
Temporary failures are retried automatically. To retry a failed delivery yourself: POST /v1/posts/{id}/deliveries/{delivery_id}/retry. Only failed deliveries can be retried; a published one is never sent again.
Preflight
POST /v1/posts/preflight takes the same body as creating a post and tells you, per destination, whether it would go through, without creating anything. It uses the same rules as ShareSomeone itself.
{"object": "preflight", "ok": false, "credit_cost": 1, "destinations": [
{"destination": "dest_…", "network": "instagram", "status": "skipped", "reasons": ["Instagram skipped — this post needs a photo or video."], "required_fields": []},
{"destination": "dest_…", "network": "youtube", "status": "requires_input", "reasons": [], "required_fields": ["Add a title for YouTube."]},
{"destination": "dest_…", "network": "x", "status": "compatible", "reasons": [], "required_fields": []}
]}
Statuses: compatible, requires_input (add the listed fields), incompatible (change the post, e.g. shorten the text), skipped (this destination can't take this kind of post).
Webhooks
Add an https endpoint (POST /v1/webhooks {"url": "https://…", "events": [...]} or on the Developers page) to receive events instead of polling. The response includes the signing secret, once.
Events: post.created, post.published, post.partially_published, post.failed, delivery.published, delivery.failed, delivery.needs_attention. Each request is a JSON event {"id": "evt_…", "type": "…", "api_version": "…", "created": "…", "data": {…}} with headers Someone-Signature, Someone-Event-Id and Someone-Delivery-Id.
Verify every request: Someone-Signature: t=<unix seconds>,v1=<hex>, where hex is HMAC-SHA256 of t + "." + raw body with your secret. Reject requests whose t is more than 5 minutes old, and use the event id to ignore duplicates.
// Node.js
import crypto from "node:crypto";
function verify(secret, rawBody, header) {
const m = /^t=(\d+),v1=([0-9a-f]{64})$/.exec(header || "");
if (!m || Math.abs(Date.now() / 1000 - Number(m[1])) > 300) return false;
const expected = crypto.createHmac("sha256", secret).update(`${m[1]}.${rawBody}`).digest("hex");
return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(m[2]));
}
# Python
import hmac, hashlib, re, time
def verify(secret: str, raw_body: bytes, header: str) -> bool:
m = re.fullmatch(r"t=(\d+),v1=([0-9a-f]{64})", header or "")
if not m or abs(time.time() - int(m.group(1))) > 300:
return False
expected = hmac.new(secret.encode(), m.group(1).encode() + b"." + raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, m.group(2))
Answer with any 2xx status within 10 seconds. Failed deliveries are retried after about 1 minute, 5 minutes, 30 minutes, 2 hours, 6 hours and 12 hours (7 attempts), then marked failed; see them with GET /v1/webhooks/{id}/deliveries. Send a test event with POST /v1/webhooks/{id}/test. Endpoints must be public https addresses.
Credits
Each live post you create with the API uses 1 credit, however many destinations it goes to: one post to five networks is one post. GET /v1/credits shows your balance (balance), what a post uses (cost_per_post) and recent changes; preflight's credit_cost tells you before you post.
- Free: test-mode posts, preflight, listing destinations, reading posts and deliveries, scheduling itself, retrying failed deliveries, and managing keys and webhooks.
- Charged once: the credit is taken when the post is created, including a scheduled post (never again when it is sent). Replaying a request with the same
Idempotency-Keynever uses another credit. - Given back: if the post can't be created; if none of its destinations publishes; or if you cancel a scheduled post before it is sent. If at least one destination publishes, the credit is used, even when other destinations fail. Retrying a post whose credit was given back takes that one credit again.
- A post your balance can't cover is refused with
402 insufficient_creditsand nothing is created.
Credits and SOMEONE Pro. Live API posts need an active SOMEONE Pro membership (402 pro_required otherwise). API credits are separate from Pro's posts: API posts never count toward the 1,000 posts a month included with Pro, and posts made on sharesomeone.com never use credits.
Rate limits
Each key may make up to 120 reads and 30 posts or other changes per minute (60 changes per account across keys); uploads have their own limits. Responses include RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset (seconds). Over the limit you get 429 rate_limited with Retry-After. Networks' own limits still apply to every post.
Errors
HTTP/1.1 400
X-Request-Id: req_…
{"error": {"code": "destination_not_found", "message": "One of the destinations wasn't found.",
"request_id": "req_…", "fields": [{"field": "destinations[0]", "message": "Unknown destination id."}]}}
Every response has an X-Request-Id; include it when you contact support. Codes are stable; messages may change. Common codes: authentication_required, invalid_api_key, api_key_revoked, insufficient_scope, invalid_json, request_too_large, destination_not_found, media_not_found, post_not_found, idempotency_key_required, idempotency_conflict, request_in_progress, invalid_schedule, insufficient_credits, pro_required, delivery_not_retryable, rate_limited, internal_error.
Idempotency
Every POST /v1/posts needs an Idempotency-Key header (8–100 letters, digits, - or _; a UUID is ideal). If your request times out, send it again with the same key: you get the original post back ("idempotent_replay": true), never a second post and never a second charge. Reusing a key with a different body returns 409 idempotency_conflict. If the original request failed (for example a validation error), the key can be used again.
Security
- Keep keys on your server. Never put a key in a browser, a mobile app or a public repository. Revoke a key immediately if it may have leaked.
- Give each key only the permissions it needs.
- Verify webhook signatures and timestamps; treat event ids as unique.
- ShareSomeone never gives out your networks' passwords or tokens, and API responses and webhooks never contain them.