## Pages Ayrshare website pages, in Markdown: - [Ayrshare Social Media API](https://www.ayrshare.com/index.md): Ayrshare is a unified social media API to post, schedule, and analyze across 14+ networks, with an MCP server for AI agents. Start your 28-day free trial. - [About Ayrshare](https://www.ayrshare.com/about-ayrshare.md): Ayrshare is a remote software company building the compliant, unified social media API other products rely on. - [Ayrshare for AI Agents](https://www.ayrshare.com/ai-agent.md): Give your AI agents the full social loop. Ayrshare lets an agent learn a brand's voice, validate every post against each platform's rules, and publish across 14+ networks in one call — over MCP or the REST API. - [The Share](https://www.ayrshare.com/blog.md): Read Ayrshare API guides, platform updates, and product notes. - [Bluesky API](https://www.ayrshare.com/bluesky-api.md): Use Ayrshare's Bluesky API to publish text, images, video, and links, schedule them, and get analytics for your users, through one REST API for 14+ networks. - [Business Plan for Multiple Users](https://www.ayrshare.com/business-plan-for-multiple-users.md): Easy-to-integrate social media APIs let you manage all of your users' social accounts right from your product. Post, auto schedule, and analytics. Great for SaaS, CMS, DAM, agencies, and apps. - [Meet our customers](https://www.ayrshare.com/case-study.md): See how AI products, SaaS platforms, agencies, and real-estate teams ship social publishing on Ayrshare. - [Ayrshare Comparisons](https://www.ayrshare.com/compare.md): Compare Ayrshare alternatives by pricing model, API access, and use case. Find the right social media API or dashboard, then explore each head-to-head guide. - [Ayrshare vs Zernio](https://www.ayrshare.com/compare/ayrshare-vs-zernio.md): We compare Ayrshare and Zernio on pricing, networks, rate limits, and features to help you find the right social media API for the product you’re building. - [Ayrshare vs Buffer](https://www.ayrshare.com/compare/buffer-api-alternative.md): Does Buffer have an API? Compare Buffer’s GraphQL API, MCP, limits, and pricing with Ayrshare’s social media API for publishing inside your own product. - [Ayrshare vs Bundle.social](https://www.ayrshare.com/compare/bundle-social-api-alternative.md): Compare Ayrshare and Bundle.social on pricing, network coverage, publishing, and MCP support to find the right social media API for the product you’re building. - [Ayrshare vs Cloud Campaign](https://www.ayrshare.com/compare/cloud-campaign-api-alternative.md): Compare the Cloud Campaign API and white-label dashboard with Ayrshare’s embedded publishing API. Explore customer connections, features, and pricing. - [Ayrshare vs Data365](https://www.ayrshare.com/compare/data365-api-alternative.md): Compare the Data365 API with Ayrshare on public-data extraction, connected-account publishing, pricing, and when it makes sense to use both APIs together. - [Ayrshare vs. Hootsuite](https://www.ayrshare.com/compare/hootsuite-api-alternative.md): Compare the Hootsuite API with Ayrshare for in-app social publishing. Explore API access, terms, analytics, and per-seat vs. per-profile pricing for your app. - [Ayrshare vs Later](https://www.ayrshare.com/compare/later-api-alternative.md): Looking for a Later API alternative? Compare Later Social’s scheduling dashboard and Influence Reporting API with Ayrshare’s embeddable publishing API. - [Ayrshare vs Loomly](https://www.ayrshare.com/compare/loomly-api-alternative.md): Looking for a Loomly API alternative? Compare Loomly’s calendar and integrations with Ayrshare’s publishing API, hosted MCP server, and profile-based pricing. - [Ayrshare vs Mallary](https://www.ayrshare.com/compare/mallary-api-alternative.md): Compare the Mallary API and Ayrshare on pricing, customer onboarding, post history, messaging, networks, and MCP support to find the right fit for your SaaS. - [Ayrshare vs Metricool](https://www.ayrshare.com/compare/metricool-api-alternative.md): Compare the Metricool API and Ayrshare on access, account connections, pricing, and publishing to find the right social media API for your customer-facing app. - [Ayrshare vs OneAll](https://www.ayrshare.com/compare/oneall-alternative.md): Looking for a OneAll alternative? Compare OneAll social login with Ayrshare’s publishing API, including pricing, overlapping features, and using both together. - [Ayrshare vs Outstand](https://www.ayrshare.com/compare/outstand-api-alternative.md): Compare Ayrshare and Outstand on pricing, publishing, network coverage, and MCP support, then choose the social media API that fits the product you’re building. - [Ayrshare vs Phyllo](https://www.ayrshare.com/compare/phyllo-api-alternative.md): Looking for a Phyllo API alternative? Compare Ayrshare and Phyllo on creator data, publishing, and pricing, and see where using both APIs together makes sense. - [Ayrshare vs Publer](https://www.ayrshare.com/compare/publer-api-alternative.md): Compare the Publer API and Ayrshare on account connections, pricing, rate limits, and publishing. Find the right fit for your team or customer-facing product. - [Ayrshare vs Sendible](https://www.ayrshare.com/compare/sendible-api-alternative.md): Compare Sendible’s SSO integration and branded dashboard with Ayrshare’s publishing API. Explore account connections, developer tools, pricing, and white label. - [Ayrshare vs SocialOomph](https://www.ayrshare.com/compare/socialoomph-api-alternative.md): SocialOomph has shut down. Explore Ayrshare as a SocialOomph alternative for API publishing, RSS auto-posting, recurring content, and customer accounts. - [Ayrshare vs SocialPilot](https://www.ayrshare.com/compare/socialpilot-api-alternative.md): Compare SocialPilot’s Enterprise API access with Ayrshare’s embedded publishing API. Explore account connections, white label, pricing, and developer workflows. - [Ayrshare vs Sprout Social](https://www.ayrshare.com/compare/sprout-social-api-alternative.md): Compare Ayrshare and Sprout Social on embedded API access, pricing, publishing, analytics, and multi-tenant social integrations for the product you’re building. - [Ayrshare vs. Twilio](https://www.ayrshare.com/compare/twilio-api-alternative.md): Does Twilio support social media posting? Compare Twilio’s messaging APIs with Ayrshare’s social publishing API, and see how to use both in your product. - [Ayrshare vs Upload-Post](https://www.ayrshare.com/compare/upload-post-api-alternative.md): Looking for an Upload-Post alternative? Compare pricing, video publishing, network coverage, post history, and validation for your multi-user social media app. - [Ayrshare vs Zoho Social](https://www.ayrshare.com/compare/zoho-alternative.md): Compare the Zoho Social API with Ayrshare on publishing, customer connections, MCP support, and pricing, and find the right fit for your product or your team. - [Ayrshare Connect](https://www.ayrshare.com/connect.md): Let your users connect Instagram, LinkedIn, TikTok and 14+ networks without leaving your app. One script tag, your styling, OAuth and token refresh handled by the Ayrshare API. - [Contact Ayrshare](https://www.ayrshare.com/contact.md): Talk to Ayrshare about social media API architecture, volume pricing, compliance, and BYOK onboarding. - [The Social Media API for Creator Data](https://www.ayrshare.com/creator-data-api.md): Access powerful influencer marketing and creator capabilities with Ayrshare's Creator Data API. Easily gather essential social media account data. - [Data Processing Agreement](https://www.ayrshare.com/data-processing-agreement.md): Ayrshare's GDPR-compliant Data Processing Agreement, including sub-processors, security, and international data transfer terms. - [Enterprise](https://www.ayrshare.com/enterprise.md): Ayrshare is the enterprise social media API trusted for 30M+ daily calls across 14+ networks, with a 99.9% API SLA, GDPR compliance, pre-publish checks, and 24/7 support. - [Facebook API](https://www.ayrshare.com/facebook-api.md): Use Ayrshare's Facebook API to publish posts, reels, and stories, schedule them, and get analytics for your users, without waiting on Meta's app review. - [Submit a Feature Request](https://www.ayrshare.com/feature-request.md): Have an idea to improve the Ayrshare social media API? Submit a feature request or integration idea to help us build a better developer experience. - [Google Business Profile API](https://www.ayrshare.com/google-business-profile-api.md): Use Ayrshare's Google Business Profile API to publish events and offers, schedule them, and get analytics for your users, without waiting on Google's approval. - [The Social Media History API](https://www.ayrshare.com/history-api.md): Retrieve past posts and engagement data across 14+ social networks with one API call, including posts published outside Ayrshare. Train AI on real brand voice, no cold start. - [Instagram API](https://www.ayrshare.com/instagram-api.md): Use Ayrshare's Instagram API to publish photos, reels, and stories, schedule them, and get analytics for your users, without waiting on Meta's app review. - [Instagram Banned Hashtag Checker](https://www.ayrshare.com/instagram-banned-hashtag-checker.md): Free Instagram banned hashtag checker. Paste your hashtags to instantly see which ones are banned or restricted before they get your account shadowbanned. Updated for 2026. - [Install Ayrshare using AI](https://www.ayrshare.com/install.md): Paste one prompt into Claude Code, Codex, Cursor or Gemini CLI and your coding assistant integrates the Ayrshare social media API for you, safely. - [Bubble Integration](https://www.ayrshare.com/integration/bubble.md): Add social media publishing, analytics, and account management to your Bubble app with the Ayrshare Bubble plugin and API Connector. - [Hurl Integration](https://www.ayrshare.com/integration/hurl.md): Test every Ayrshare endpoint from your terminal with the Ayrshare Hurl scripts for images, video, analytics, and more. - [Notion Integration](https://www.ayrshare.com/integration/notion.md): Post to your social accounts from a Notion no-code database, pick platforms and media, and schedule directly inside your workflow. - [Oracle Integration](https://www.ayrshare.com/integration/oracle.md): Post to all your social accounts from Oracle Content Management and extend OCM with Ayrshare analytics and historical data. - [PyPI Integration](https://www.ayrshare.com/integration/pypi.md): Use the Social-Post-API PyPI package to call Ayrshare from Python on the server-side across Premium, Business, and Enterprise plans. - [Social Media Auto Poster WordPress Plugin](https://www.ayrshare.com/integration/wordpress.md): Automatically publish your WordPress posts to all of your social media accounts with the official Ayrshare WordPress plugin. - [Ayrshare Integrations](https://www.ayrshare.com/integrations.md): Connect Ayrshare with the platforms your team already uses. Schedule posts, pull analytics, add comments, and more through no-code tools, SDKs, and direct social network APIs. - [LinkedIn API](https://www.ayrshare.com/linkedin-api.md): Use Ayrshare's LinkedIn API to publish text, images, video, and PDFs, schedule them, and get analytics for your users, without waiting on LinkedIn's approval. - [Oracle Content Management Social Media Posting Guide](https://www.ayrshare.com/oracle-content-management-social-media-posting-guide.md): Step-by-step setup for publishing to social media from Oracle Content Management (OCM) with Ayrshare: link your accounts, configure the asset type, add the webhook, and publish across every network. - [Ayrshare Pricing](https://www.ayrshare.com/pricing.md): Embed the Ayrshare Social Media API into your product and pay for active social profiles, not users, seats, or internal team members. As your customers connect social accounts, your usage grows with them. When a customer churns, your costs go back down. - [Privacy Policy](https://www.ayrshare.com/privacy.md): Read Ayrshare's privacy policy and data handling practices. - [Automatically Post Your Newsletter to Your Social Media](https://www.ayrshare.com/rss-feed-substack-socialmedia.md): Automatically Post Your Substack Newsletter to Your Social Media - [Snapchat API](https://www.ayrshare.com/snapchat-api.md): Use Ayrshare's Snapchat API to publish stories and Spotlight videos, schedule them, and get analytics for your users, without waiting on Snap's allowlist. - [Social Media Analytics API](https://www.ayrshare.com/social-media-analytics-api.md): Get real-time social media analytics through one API. Pull likes, shares, comments, impressions, video views, historical data, and link click analytics across 14+ networks. - [Social Media Analytics Metrics](https://www.ayrshare.com/social-media-analytics-metrics.md): Ayrshare's Analytics API returns 715 metrics across 12 networks: retention curves, reaction breakdowns, demographics, story taps. The full reference. - [Social Media Messaging API](https://www.ayrshare.com/social-media-messenger-apis.md): Get to market fast and scale effortlessly with the Ayrshare Messaging API for social media. Enable conversations that drive sales, engagement, and customer support across Facebook Messenger, Instagram, and X. - [The Social Media Publishing API](https://www.ayrshare.com/social-media-publishing-api.md): Publish and schedule posts, Reels, Stories, and video across 14+ social networks with one API call. Approval workflows, pre-publish validation, and webhooks built in. - [API Solutions](https://www.ayrshare.com/solutions.md): Step-by-step fixes for common Instagram, Meta, X, LinkedIn, TikTok and YouTube API errors (400, 401, 403, 429), plus guides to API version migrations. - [Cross-Platform API Error 400: Duration & Metadata Validation Failed](https://www.ayrshare.com/solutions/cross-platform-api-error-400-duration-metadata-validation-failed.md): Posting one video to Instagram, YouTube, and TikTok at once triggers different duration and metadata errors on each. Ayrshare explains why, and the exact fix. - [Google API Error 403: Unverified App & How to Fix the Audit Pipeline](https://www.ayrshare.com/solutions/google-api-error-403-unverified-app-how-to-fix-the-audit-pipeline.md): Google API Error 403 on an unverified app? See why the OAuth audit and quota sandbox happen, and how Ayrshare's pre-verified access skips it entirely for you. - [Google Business API Error 403: Location Not Verified & How to Fix It](https://www.ayrshare.com/solutions/google-business-api-error-403-location-not-verified-how-to-fix-it.md): Google Business Profile posts failing with a 403 PERMISSION_DENIED? See why unverified locations get blocked and how Ayrshare handles the pre-flight check. - [Handling TikTok Rate Limits: Resolving the 429 Too Many Requests Error](https://www.ayrshare.com/solutions/handling-tiktok-rate-limits-resolving-the-429-too-many-requests-error.md): TikTok enforces a strict sliding-window rate limit that can lock out bursty traffic instantly. Ayrshare explains the mechanics and exactly how to avoid a 429. - [How to Bypass the YouTube 10k Unit Limit: Fixing the 403 Quota Exceeded Error](https://www.ayrshare.com/solutions/how-to-bypass-the-youtube-10k-unit-limit-fixing-the-403-quota-exceeded-error.md): YouTube's default 10,000 daily quota units allow only six video uploads before a 403 quotaExceeded error. Ayrshare explains why, and how to bypass it entirely. - [How to Fix Instagram API Error 100: "Media ID Not Found" and "Unknown Error"](https://www.ayrshare.com/solutions/how-to-fix-instagram-api-error-100-media-id-not-found-and-unknown-error.md): Instagram API Error 100 is almost always a race condition between container creation and publishing. See why it happens and how Ayrshare prevents it for good. - [How to Fix Meta Error 200: "Permissions Error" and Insufficient Scopes](https://www.ayrshare.com/solutions/how-to-fix-meta-error-200-permissions-error-and-insufficient-scopes.md): Meta Error 200 usually means Standard Access, a User Token instead of a Page Token, or a missing scope. Ayrshare explains the causes and how to fix each one. - [Instagram API Error 10: Application Permission Denied & How to Fix Direct Publishing](https://www.ayrshare.com/solutions/instagram-api-error-10-application-permission-denied-how-to-fix-direct-publishing.md): Instagram API Error 10 means the connected account is Personal, not Business or Creator. Ayrshare explains the account-type rule and exactly how to handle it. - [Instagram API Error 100: Carousel Validation Failed & How to Fix It](https://www.ayrshare.com/solutions/instagram-api-error-100-carousel-validation-failed-how-to-fix-it.md): Instagram carousel posts failing with Error 100 or invalid_children? See why nested container validation fails and how Ayrshare handles it automatically today. - [Instagram Graph API Error 9: The 25-Post Daily Limit & How to Fix It](https://www.ayrshare.com/solutions/instagram-graph-api-error-9-the-25-post-daily-limit-how-to-fix-it.md): Instagram's Graph API caps publishing at 25 posts per rolling 24-hour window. See why Error 9 triggers and how Ayrshare manages the limit automatically for you. - [LinkedIn 403: "Unpermitted Access" (URN Mismatches & Permissions)](https://www.ayrshare.com/solutions/linkedin-403-unpermitted-access-urn-mismatches-permissions.md): LinkedIn API 403 errors usually mean a person/organization URN mismatch or missing Marketing Developer Platform access. Ayrshare explains why, and the fix. - [LinkedIn API Error 401: Invalid Grant & How to Fix Token Expiration](https://www.ayrshare.com/solutions/linkedin-api-error-401-invalid-grant-how-to-fix-token-expiration.md): LinkedIn access tokens expire after 60 days, triggering invalid_grant errors. Ayrshare explains the refresh flow and how to avoid manual token rotation today. - [LinkedIn API v1 to v2 (Versioned) Migration: The Developer’s Survival Guide](https://www.ayrshare.com/solutions/linkedin-api-v1-to-v2-versioned-migration-the-developers-survival-guide.md): Migrating from LinkedIn's legacy API to the Versioned API (v2/rest)? Ayrshare covers the base URL, header, and payload changes, and how to avoid them entirely. - [LinkedIn & Meta API Error 400: Invalid Image Dimensions & How to Fix It](https://www.ayrshare.com/solutions/linkedin-meta-api-error-400-invalid-image-dimensions-how-to-fix-it.md): LinkedIn and Meta reject images that don't match a 1.91:1 aspect ratio. Ayrshare explains the Invalid Image Dimensions error and how auto-resizing fixes it. - [Meta Graph API Versioning Survival Kit: Staying Ahead of v21, v22, and Beyond](https://www.ayrshare.com/solutions/meta-graph-api-versioning-survival-kit-staying-ahead-of-v21-v22-and-beyond.md): Meta Graph API versions sunset on a 24-month clock, and Marketing API versions even faster. Ayrshare explains what breaks and how to stop tracking it yourself. - [Meta & LinkedIn API Error 401: Expired Token & How to Fix It](https://www.ayrshare.com/solutions/meta-linkedin-api-error-401-expired-token-how-to-fix-it.md): Meta and LinkedIn access tokens expire on a strict TTL, and manual refresh workers can hit race conditions. Ayrshare explains why, and how to avoid it entirely. - [Meta & Stripe API Error 401: Signature Mismatch & How to Fix Webhook Security](https://www.ayrshare.com/solutions/meta-stripe-api-error-401-signature-mismatch-how-to-fix-webhook-security.md): Getting a 401 or Signature Verification Failed on your Meta webhook? See why raw-body hashing fails and how Ayrshare's unified webhook signature simplifies it. - [Meta & X API Error 429: Polling Rate Limits & How to Fix Event Syncing](https://www.ayrshare.com/solutions/meta-x-api-error-429-polling-rate-limits-how-to-fix-event-syncing.md): Polling Meta or X for updates instead of using webhooks triggers 429 errors fast. Ayrshare explains the webhook alternative and how to secure it the right way. - [Moving from Twitter v1.1 to v2: Bridging the “JSON Gap”](https://www.ayrshare.com/solutions/moving-from-twitter-v1-1-to-v2-bridging-the-json-gap.md): Migrating from Twitter API v1.1 to X API v2 breaks JSON structure, auth, and media uploads. Ayrshare explains the changes and how to avoid them today for you. - [Pinterest API v5 Transition: The Complete Developer Guide](https://www.ayrshare.com/solutions/pinterest-api-v5-transition-the-complete-developer-guide.md): Everything you need to migrate from Pinterest API v4 to v5 — board access, refresh tokens, SDK setup, and how Ayrshare handles the complexity for you. Why You Need to Migrate to Pinterest API v5 If you are building or maintaining a social media scheduling tool, analytics dashboard, or any automation that touches Pinterest, you… - [Social Media API Error 400: Media Validation Failed & How to Fix Video Encoding](https://www.ayrshare.com/solutions/social-media-api-error-400-media-validation-failed-how-to-fix-video-encoding.md): Video uploads failing with a 400 Media Validation error? See the four codec, bitrate, and resolution rules platforms enforce, and how Ayrshare auto-fixes them. - [TikTok API 400: "Bad Request" (Encoding & Validation)](https://www.ayrshare.com/solutions/tiktok-api-400-bad-request-encoding-validation.md): TikTok API 400 errors are almost always video validation failures: bitrate, aspect ratio, duration, or codec. Ayrshare's autoResize fixes them automatically. - [TikTok API Error 10001: The Silent Shadowban & How to Fix It](https://www.ayrshare.com/solutions/tiktok-api-error-10001-the-silent-shadowban-how-to-fix-it.md): TikTok Error 10001 means bot detection has silently shadowbanned your app. Ayrshare explains exactly why it happens and how trusted infrastructure avoids it. - [TikTok & Meta API Error 400: Invalid Cover Asset & How to Fix Video Thumbnails](https://www.ayrshare.com/solutions/tiktok-meta-api-error-400-invalid-cover-asset-how-to-fix-video-thumbnails.md): TikTok, Meta, and YouTube each require a completely different video cover format. Ayrshare explains why, and how one thumbUrl works across all three platforms. - [Twitter/X Error 403: "Forbidden" (Tier Access & Scopes)](https://www.ayrshare.com/solutions/twitter-x-error-403-forbidden-tier-access-scopes.md): X API 403 errors usually trace back to your paid tier, read-only app permissions, or a v1.1/v2 endpoint mismatch. Ayrshare explains each cause and the fix. - [X & LinkedIn API Error 400: Invalid Media Metadata & How to Fix Alt-Text](https://www.ayrshare.com/solutions/x-linkedin-api-error-400-invalid-media-metadata-how-to-fix-alt-text.md): Adding image alt-text to X or LinkedIn posts triggers a 400 if the timing or JSON structure is off. Ayrshare explains the fix, or generates alt-text for you. - [X & LinkedIn API Error 400: Text Validation Failed & How to Fix Character Limits](https://www.ayrshare.com/solutions/x-linkedin-api-error-400-text-validation-failed-how-to-fix-character-limits.md): Standard string length checks fail on X and LinkedIn because of link wrapping and URN weighting. Ayrshare explains the real character math, and the exact fix. - [X (Twitter) API Error 403: Duplicate Post & How to Fix It](https://www.ayrshare.com/solutions/x-twitter-api-error-403-duplicate-post-how-to-fix-it.md): X blocks posts with matching content hashes for up to 12 hours. Ayrshare explains the anti-spam mechanics and exactly how to safely post repeat content again. - [X (Twitter) API Error 429: The Monthly Tweet Cap & How to Fix It](https://www.ayrshare.com/solutions/x-twitter-api-error-429-the-monthly-tweet-cap-how-to-fix-it.md): X enforces a separate monthly post ceiling on top of its 15-minute rate limits. Ayrshare explains how the cap works and exactly how to avoid a global outage. - [Supported Networks](https://www.ayrshare.com/supported-networks.md): See every social network Ayrshare's API supports, what you can publish on each, and the main limits, then reach all 14+ networks through one REST API call. - [Telegram API](https://www.ayrshare.com/telegram-api.md): Use Ayrshare's Telegram API to post text, links, images, and video to channels and groups, then schedule or delete them, through one REST API for 14+ networks. - [Terms of Use](https://www.ayrshare.com/terms.md): Read the Ayrshare Terms of Use governing accounts, subscriptions, site access, and acceptable use. - [Threads API](https://www.ayrshare.com/threads-api.md): Use Ayrshare's Threads API to publish text posts, carousels, and threads, schedule them, and get analytics for your users, without waiting on Meta's app review. - [TikTok API](https://www.ayrshare.com/tiktok-api.md): Publish TikTok videos and 35-photo carousels, schedule posts, and pull analytics through one REST API that also reaches 14+ social networks. - [Ayrshare for AI Startups](https://www.ayrshare.com/use-cases/ai-startups.md): The AI-native social media API that gives your agents everything they need to operate on social platforms: learn a brand's voice, publish across 14+ networks, measure, and stay compliant. - [Ayrshare for Content Creator Platforms](https://www.ayrshare.com/use-cases/content-creator-platforms.md): The multi-tenant social media API behind creator platforms: let every creator publish, schedule, and analyze across 14+ networks, with isolated profiles proven at 25M+ daily API calls. - [Ayrshare for Digital Agencies](https://www.ayrshare.com/use-cases/digital-agencies.md): The white-label social media API agencies use to launch their own branded client tools. Publish, schedule, analyze, and manage engagement across 14+ networks for hundreds of client accounts through one API. - [Ayrshare for SaaS Platforms](https://www.ayrshare.com/use-cases/saas-platforms.md): The embedded social media API for vertical SaaS. Add publishing, scheduling, analytics, and engagement across 14+ networks for thousands of your customers' accounts, with multi-tenant architecture handled for you. - [Ayrshare for Video Content Platforms](https://www.ayrshare.com/use-cases/video-content-platforms.md): Your platform creates the video. Ayrshare publishes it, short- and long-form, to YouTube, TikTok, Reels, and more in a single call, then pulls analytics back. One API for the full publishing and feedback lifecycle across 13 of 14 networks. - [X (Twitter) API](https://www.ayrshare.com/x-twitter-api.md): Ayrshare's Twitter API (X API): publish posts, threads, and polls, send DMs, search X by keyword, and monetize video through one REST API for 14+ networks. - [YouTube API](https://www.ayrshare.com/youtube-api.md): Use Ayrshare's YouTube API to publish videos and shorts, schedule them, and get analytics for your users, without waiting on Google's OAuth verification. ## Case Studies Ayrshare customer case studies, in Markdown: - [How Curbfeed Onboards a Brokerage in About a Minute Without Building a Social Platform Layer](https://www.ayrshare.com/case-study/curbfeed-real-estate.md): See how Curbfeed, a real estate marketing platform, runs its entire automated office-level publishing pipeline on Ayrshare. - [How Quso.ai Scaled to 4M Users Without a Single Engineer on Social API Maintenance](https://www.ayrshare.com/case-study/vidyo-ai.md): Find out how Quso.ai (formerly Vidyo.ai), an AI video platform serving 4M+ users, has run its entire social publishing stack on Ayrshare since January 2022. ## Blog Every Ayrshare blog post, in Markdown: - [Goodbye External Auth Pages, Hello Embedded UI: Introducing Ayrshare Connect.](https://www.ayrshare.com/blog/introducing-ayrshare-connect.md): Ayrshare Connect puts social account linking inside your own dashboard. One script tag, your styling, ten events into your code, and the Ayrshare API keeps handling OAuth and token refresh. - [The New Standard in API Reliability: Inside Our Backend Rewrite](https://www.ayrshare.com/blog/we-rebuilt-the-backend-of-our-api.md): Inside Ayrshare 3: why we rebuilt a working backend, how we migrated customer traffic without changing integrations and what the new foundation means for performance, features and fixes. - [September Release: Facebook Boosted Ads With Traffic Goals and CTA Buttons, Plus Reels Audio](https://www.ayrshare.com/blog/september-release-facebook-boosted-ads-traffic-goals-cta-buttons.md): Boost a post toward off-platform clicks and put a Shop Now button on it. Plus licensed audio on Instagram Reels, immediate monthly-plan refunds, and cached LinkedIn analytics. - [WhatsApp API Now Available in Ayrshare: Send and Receive WhatsApp Messages](https://www.ayrshare.com/blog/whatsapp-api-launch.md): The Ayrshare API now includes WhatsApp. Send messages, receive incoming messages and media, and retrieve conversations through the Messaging API. - [n8n Social Media Automation: Three Ways to Do It (and When You Need an AI Agent)](https://www.ayrshare.com/blog/n8n-social-media-automation.md): Three ways to run social media out of n8n: per-platform nodes, one REST call to a social media API, or an AI agent over MCP (and when each one fits) - [Ayrshare Is Now Native in Claude Code](https://www.ayrshare.com/blog/ayrshare-claude-code-plugin.md): An AI agent can now take a social post from draft to published across 14+ networks, with no per-platform integration code. - [Instagram Engagement Automations, Now in the Ayrshare API](https://www.ayrshare.com/blog/instagram-engagement-automations-ayrshare-api.md): The Ayrshare API now supports Instagram engagement automations: keyword-triggered DMs, webhooks, and emails that comply with Meta's policies. - [Is Your Social Media API Compliant with X’s Developer Policies?](https://www.ayrshare.com/blog/x-api-compliance-byo-app.md): X requires every third-party social media API to operate under a BYO model. Here's what that means and what compliant implementation looks like. - [Publish and Monetize X Video Content in One Workflow with Ayrshare](https://www.ayrshare.com/blog/monetize-x-video-with-ayrshare.md): Monetize your video content on X directly from your publishing workflow. With Ayrshare’s API, there’s no need to switch to Media Studio. - [Instagram Analytics Data in Ayrshare: A Product Manager’s Guide](https://www.ayrshare.com/blog/instagram-analytics-data-in-ayrshare-for-product-managers.md): Everything product managers need to know about Instagram analytics in Ayrshare: what each data type does and how to build better features. - [Coding with Multiple AI Agents to Build Scalable Rate-Limiting Infrastructure](https://www.ayrshare.com/blog/coding-with-multiple-ai-agents.md): How Ayrshare used ChatGPT, Claude Code, Gemini, and Ralph loops to refactor rate limits into a scalable, enterprise-ready policy engine. - [Threads API Integration: Authorization, Posting, & Analytics with Ayrshare](https://www.ayrshare.com/blog/threads-api-integration-authorization-posting-analytics-with-ayrshare.md): How to integrate Meta's Threads API into your platform. Easily manage your users' Threads accounts via Ayrshare's social API. - [Unlocking Real-Time Engagement with X Account Activity API via Ayrshare](https://www.ayrshare.com/blog/unlocking-real-time-engagement-with-x-account-activity-api-via-ayrshare.md): Unlock real-time engagement on X (formerly Twitter) using the Account Activity API, easily integrated via Ayrshare's powerful social media API. - [Implementing Multi-User Social Account Linking with Ayrshare](https://www.ayrshare.com/blog/implementing-multi-user-social-account-linking-with-ayrshare.md): Learn how to allow your users to link their social media accounts and manage these accounts using the social media API. - [Complete Guide to Handling API Rate Limits: Prevent 429 Errors](https://www.ayrshare.com/blog/complete-guide-to-handling-rate-limits-prevent-429-errors.md): Build robust client-side rate limiting for APIs with our 4-step JavaScript guide. Prevent 429 errors, implement retry logic, & more. - [Complete Guide to Snapchat API Integration: Authorization, Posting, & Analytics with Ayrshare](https://www.ayrshare.com/blog/complete-guide-to-snapchat-api-integration.md): The Snapchat API guide. How to integrate with Snapchat from authorizing, to publishing, to getting analytics. - [Make’s X (formerly Twitter) Integration Has Ended — Here’s a Better Alternative](https://www.ayrshare.com/blog/make-coms-x-integration-alternative.md): Make.com has deprecated its X (Twitter) integration. Use Ayrshare's social media API as an X automation alternative. - [Ayrshare Social Account Linking Page Customization Demo Video](https://www.ayrshare.com/blog/ayrshare-social-account-linking-page-customization-demo-video.md): Watch a demo video on how to customize the Ayrshare Social Account Linking page. Whitelabel the experience to perfectly match your platform's brand. - [Facebook Ads API: Boosting Facebook Posts with the Marketing API](https://www.ayrshare.com/blog/facebook-ads-api-boosting-with-the-marketing-api.md): Use the Facebook Ads API (Marketing API) to create boosted paid post directly from organic posts. Retrieve ad spend, clicks, and impressions. - [Instagram Demographics 2025: Key Audience Insights and Statistics](https://www.ayrshare.com/blog/instagram-demographics-2025-key-audience-insights-and-statistics.md): This guide provides a deep dive into the latest demographics on Instagram, including geographic, education, income, and gender distributions. - [How to Get Direct Download URLs from Dropbox](https://www.ayrshare.com/blog/how-to-get-direct-download-urls-from-dropbox.md): Learn how to transform Dropbox sharing links into direct download URLs for seamless integration with your applications and APIs. - [Top 14 Social Media APIs for Developers (2027)](https://www.ayrshare.com/blog/top-10-social-media-apis-for-developers.md): The top social media APIs developers integrate in 2027: Bluesky, Facebook, Instagram, X, LinkedIn, Pinterest, Reddit, TikTok, Telegram, YouTube and more. - [How to Get Direct Download URLs from Google Drive](https://www.ayrshare.com/blog/how-to-get-direct-download-urls-from-google-drive.md): Learn how to convert Google Drive sharing links into direct download URLs for programmatic access and API integrations. With code examples. - [HTTP Compression in Node.js: A Dive into Gzip, Deflate, and Brotli](https://www.ayrshare.com/blog/http-compression-in-node-js-a-dive-into-gzip-deflate-and-brotli.md): Reduce API payload size in Node.js using Gzip, Deflate, and Brotli compression. A guide with code examples and algorithm benchmarks. - [How to Retrieve Social Media Posts For All Your Users with an API](https://www.ayrshare.com/blog/how-to-retrieve-social-media-posts-for-all-your-users-with-an-api.md): How to Retrieve Social Media Posts For All Your Users with an API - [Complete Guide to Bluesky API Integration: Authorization, Posting, Analytics & Comments](https://www.ayrshare.com/blog/complete-guide-to-bluesky-api-integration-authorization-posting-analytics-comments.md): Learn how to integrate social media functionality with the Bluesky API in this comprehensive guide. Explore authentication, post publishing, analytics, comment management, and more to enhance your app or business. - [Get Social Media Post Analytics with Make & Ayrshare – A No-Code Quick Video Tutorial](https://www.ayrshare.com/blog/get-social-media-post-analytics-with-make-ayrshare-a-no-code-quick-tutorial.md): Watch this quick no-code tutorial to learn how to retrieve social media post analytics automatically using Make (Integromat) and Ayrshare. - [Testing Your Social Posting Integration With Random Content](https://www.ayrshare.com/blog/testing-your-social-posting-integration-with-random-content.md): Learn how to test your social media posting API integration using random text and image content to ensure a seamless setup before going live. - [Never Miss a Comment Again: How to Use Ayrshare’s Comments API Endpoint](https://www.ayrshare.com/blog/never-miss-a-comment-again-how-to-use-ayrshares-comments-api-endpoint.md): Learn how to use Ayrshare's API Comment Endpoint to publish, reply, delete, hide comments on TikTok, Facebook, Instagram, LinkedIn, YouTube. - [Create an API Proxy Server for No-Code](https://www.ayrshare.com/blog/create-an-api-proxy-server-for-no-code.md): Create a serverless API proxy sever for your no-code application that allows you to transform the API data response. - [10 Recent Enhancements to the Ayrshare Dashboard](https://www.ayrshare.com/blog/10-recent-enhancements-to-the-ayrshare-dashboard.md): Recent enhancements to Ayrshare Social Media API Dashboard. - [Ayrshare Reviews](https://www.ayrshare.com/blog/ayrshare-reviews.md): What you should look for when evaluating reviews for a potential SaaS partner, including Ayrshare Reviews. - [Top 7 Social Sign-On APIs For 2024](https://www.ayrshare.com/blog/top-7-social-login-apis.md): Reduce the friction of login, increase security with the top social sign-on APIs: Facebook, Google, X, and GitHub. - [Ayrshare Launches Messaging API for Facebook, Instagram, and X Direct Messages](https://www.ayrshare.com/blog/ayrshare-launches-messaging-api-for-facebook-instagram-and-x-direct-messages.md): Ayrshare's social media API now included the Messaging API for Facebook Messenger, Instagram Message, and X DMs. - [Best Practices for API Design](https://www.ayrshare.com/blog/best-practices-for-api-design.md): Learn how to create a superior API design. The best strategies for creating scalable, secure, and developer-friendly APIs. - [Build a Social Media Posting App with No Code](https://www.ayrshare.com/blog/build-a-social-media-posting-app-with-no-code.md): A step-by-step guide to building your own social media posting app without code, using a no-code platform or an AI agent on top of the Ayrshare API. - [How to Query for Non-Existent Fields in Firestore](https://www.ayrshare.com/blog/how-to-query-for-non-existent-fields-in-firestore.md): How do you query Firebase's Firestore database for non-existent field? While there is no direct way, so work-arounds exist. - [5 Tips for Great Developer API Docs](https://www.ayrshare.com/blog/5-tips-for-great-developer-api-docs.md): The 5 top tips on how to write great developer API docs. With this guide you'll learn how to get developers excited about your API. - [Ayrshare Messaging API for Facebook, Instagram, and X/Twitter](https://www.ayrshare.com/blog/ayrshare-messaging-api-for-facebook-instagram-and-x-twitter.md): Ayrshare Messaging API for real-time direct messaging on Facebook, Instagram, and X/Twitter. Easily end and received DMs. - [User Profile Tagging in Ayrshare](https://www.ayrshare.com/blog/user-profile-tagging-in-ayrshare.md): Learn how to enable user profile tagging in your social media posts using Ayrshare's API. Tag users across multiple social networks seamlessly. - [Understanding the Ayrshare Auto-Schedule API Endpoint](https://www.ayrshare.com/blog/understanding-the-ayrshare-auto-schedule-api-endpoint.md): Auto schedule your social media post via an API. Automate the scheduling of posts with set pre-defined posting times and days. - [Why Do I Have to Test Your Code? (The 5-Minute Sniff Test)](https://www.ayrshare.com/blog/why-do-i-have-to-test-your-code-the-5-minute-sniff-test.md): If you can’t spend 5 minutes testing your work, then don’t make me. Why an software engineer need to test before handing to QA. - [The Top 5 Chakra UI Tips and Tricks for React Developers](https://www.ayrshare.com/blog/the-top-5-chakra-ui-tips-and-tricks-for-react-developers.md): If you're using Chakra UI with React, here are our top 5 tips and trick getting the most of out Chakra UI. - [Get Public Social Media Analytics, A No Code Tutorial Video](https://www.ayrshare.com/blog/get-public-social-media-analytics-a-no-code-tutorial-video.md): Follow this no-code video tutorial to easily gather public social media analytics from multiple networks using the Ayrshare API. - [Facebook Removes Groups API Access: Impact and Implications](https://www.ayrshare.com/blog/facebook-removes-groups-api-access-impact-and-implications.md): Facebook has announced that it will remove access to its Groups API. We discuss the impact and implications. - [Instagram Collaborator API](https://www.ayrshare.com/blog/instagram-collaborator-api.md): Add collaborators to Instagram posts using an API. These posts will show in the collaborator's feed and be sent to their followers. - [Best Social Media Posting APIs for SaaS and AI Products](https://www.ayrshare.com/blog/best-social-media-posting-and-scheduling-apis.md): Ten social media posting and scheduling APIs, sorted by the job they do: automating your own accounts or letting every customer connect theirs. Pricing, platform coverage, MCP support, and a production-readiness checklist. - [Reviews API: Review Management with an API](https://www.ayrshare.com/blog/reviews-api-review-management-with-an-api.md): Reviews management with the Reviews API. Easily manage your Facebook Page and Google Business Profile review with an API. - [Instagram Banned Hashtags to Avoid (Updated 2026)](https://www.ayrshare.com/blog/avoid-using-these-instagram-banned-hashtags.md): The definitive list of Instagram banned hashtags to avoid so your account isn't locked or shadow banned. Originally published February 2024, updated for 2026. - [What is a Webhook? How They Work With Examples](https://www.ayrshare.com/blog/what-is-a-webhook-how-they-work-with-examples.md): What is a webhook and how do they work? Learn all about webhooks from registering a webhook URL to calling a webhook. - [TikTok API : How to Post and Get Analytics Using the TikTok API](https://www.ayrshare.com/blog/tiktok-api-how-to-post-and-get-analytics.md): How to use the TikTok API to publish posts and get analytics for your users. - [Ayrshare Quick Start Guide [video]](https://www.ayrshare.com/blog/ayrshare-quick-start-guide-video.md): We take you through the Ayrshare Quick Start Guide to explain how to get started with the Ayrshare social media API. - [How to Use Firebase Queues](https://www.ayrshare.com/blog/how-to-use-firebase-queues.md): What are Firebase Queues? And how do we use them? - [Streamlining Webhook Development: How a Webhook Proxy Tool Can Transform Your Workflow](https://www.ayrshare.com/blog/streamlining-webhook-development-how-a-webhook-proxy-tool-can-transform-your-workflow.md): How to use a webhook relay proxy to simplify local webhook development and testing. - [Instagram Stories API: How to Publish a Story](https://www.ayrshare.com/blog/instagram-stories-api-how-to-post-a-story.md): We are excited to announce Ayrshare as the only API-first platform to offer direct posting to Instagram Stories via an API. - [Automatically Import All Your Social Media Analytics | No Code Tutorial Video](https://www.ayrshare.com/blog/automatically-import-all-your-social-media-analytics-no-code-tutorial-video.md): Learn how to use no-code Parabola and Ayrshare to get Facebook and X/Twitter social media analytics into a Google Sheet. - [How to Publish Stories with the Facebook Stories API](https://www.ayrshare.com/blog/how-to-publish-stories-with-the-facebook-stories-api.md): Learn how to publish Facebook Stories, both video and photos, using the Meta Stories API. - [Connect Your Social Accounts and Post via the Ayrshare Dashboard [video]](https://www.ayrshare.com/blog/connect-your-social-accounts-and-post-via-the-ayrshare-dashboard-video.md): Learn how to connect your social media accounts, then create a video post which goes out to 5 social networks. - [Google Business Profile: What is GBP, Why You Need It, and How to Use It?](https://www.ayrshare.com/blog/google-my-business-what-is-gmb-why-you-need-it-and-how-to-use-it.md): Google Business Profile is a powerful SEO tool for businesses. Find out what GBP is, why you need it, and how to use it with an API. - [Automatically Generate Image Caption Text Using Google Cloud Vision API with JavaScript and Node.js](https://www.ayrshare.com/blog/automatically-generate-image-caption-text-using-google-cloud-vision-api-with-javascript-and-node-js.md): Learn how to use the Google Cloud Vision API to generate image captions automatically and alt text, with code examples in JavaScript. - [Navigating the Intricacies of Social Media API Integration: Top 5 Challenges and How to Overcome Them](https://www.ayrshare.com/blog/navigating-the-intricacies-of-social-media-api-integration-top-5-challenges-and-how-to-overcome-them.md): What are the top social media API challenges? Learn how to navigate these challenges and easily integrate with the social networks. - [Max Pack: Take Your Social Media Marketing to the Next Level](https://www.ayrshare.com/blog/max-pack-take-your-social-media-marketing-to-the-next-level.md): The Max Pack is a powerful add-on to the Ayrshare Social API with even more capabilities to automate and improve your media marketing. - [The Importance of Alt Text: Making Images Accessible and SEO-Friendly](https://www.ayrshare.com/blog/the-unseen-importance-of-alt-text-making-images-accessible-and-seo-friendly.md): Learn why adding alt text to your website's images is crucial for accessibility and SEO + how to automatically generate alt text with an API. - [Build a Social Media Posting App in No-Code Platform FlutterFlow](https://www.ayrshare.com/blog/build-a-social-media-posting-app-in-no-code-platform-flutterflow.md): Learn how to use no-code Flutterflow to build a social media posting app that publishes both text and images. - [Leveraging Ayrshare: Go Beyond Basic Social Media API Integration](https://www.ayrshare.com/blog/leveraging-ayrshare-go-beyond-basic-social-media-api-integration.md): Learn why Ayrshare's social media APIs are often the right choice over directly integrating social APIs such the TikTok API or Instagram API. - [Rewrite Your Text with AI and Post to Your Social Networks [Video]](https://www.ayrshare.com/blog/rewrite-your-text-with-ai-and-post-to-your-social-networks-video.md): Learn of to use Ayrshare and no-code Bubble.io to build a AI rewriting and social posting app. - [How to Schedule Twitter Threads with the Twitter API](https://www.ayrshare.com/blog/how-to-schedule-twitter-threads-with-the-twitter-api.md): How to schedule your Twitter threads using the Twitter API with JavaScript and Node.js. - [The Instagram API Just Went to 11](https://www.ayrshare.com/blog/the-instagram-api-just-went-to-11.md): Instagram just updated their API with Creator account publishing, increase posting limit, user tagging, audio naming, and cover images. - [Artificial Intelligence Anxiety Around the U.S.](https://www.ayrshare.com/blog/ai-anxiety-around-the-us.md): The use of AI has been a hot topic since ChatGPT's release in 2022. Find out what Americans have to say about their fears surrounding AI at work here. - [Transcribe Your Audio or Video File, Summarize it, and Post it to Social in 4 Easy API Calls [video]](https://www.ayrshare.com/blog/transcribe-your-audio-or-video-file-summarize-it-and-post-it-to-social-in-4-easy-api-calls-video.md): Transcribe Your Audio or Video File, Summarize it, and Post it to Social in 4 Easy API Calls - [Ayrshare Unleashes Game-Changing Integrations for TikTok Direct Video Publishing & Twitter Enterprise API](https://www.ayrshare.com/blog/ayrshare-unleashes-game-changing-integrations-for-tiktok-direct-video-publishing-twitter-enterprise-api.md): Ayrshare announced 65 new features across the 10 most popular social networks including special API access to direct TikTok posting. - [Build A Twitter Analytics App in Retool [video]](https://www.ayrshare.com/blog/build-a-twitter-analytics-app-in-retool-video.md): Build A Twitter Analytics App in Retool - [Introducing TikTok Direct Publishing, Analytics, and Commenting](https://www.ayrshare.com/blog/introducing-tiktok-direct-publishing-analytics-and-commenting.md): Ayrshare now has automatic TikTok publishing, advanced post analytics, and managing comments with the TikTok API. - [The Best Hashtag Generator and Research Tools](https://www.ayrshare.com/blog/the-7-best-hashtag-generator-and-research-tools.md): Hashtags help reach your target audience. These are the best tools to generate the relevant hashtags for your social media posts. - [Creating Social Media Posts with ChatGPT API](https://www.ayrshare.com/blog/creating-social-media-posts-with-chatgpt-api.md): How to use the ChatGPT API to post to social media networks such as Instagram and Facebook. Combine AI with automated social posting. - [Facebook API: How to Post and Get Analytics Using the Facebook API](https://www.ayrshare.com/blog/facebook-api-how-to-post-and-get-analytics-using-the-facebook-api.md): Learn how the Facebook API can enhance your business and how to post or get analytics from your platform or app on behalf of your users. - [Understanding the Difference Between the Instagram Business, Creator, and Personal Profiles](https://www.ayrshare.com/blog/understanding-the-difference-between-the-instagram-business-creator-and-personal-profiles.md): What is the difference between the Instagram Business, Creator, and Personal Profiles, when do you want to use each, and what are the considerations when using the Instagram API. - [Speed Up Firebase Cold Starts with Firestore REST Calls](https://www.ayrshare.com/blog/speed-up-firebase-cold-starts-with-firestore-rest-calls.md): Firestore now has the preferRest option to speed up Firebase cold start up times. Here is how to use the new feature. - [Schedule Social Media Posts from Notion](https://www.ayrshare.com/blog/schedule-social-media-posts-from-notion.md): Discover how to automatically schedule and publish your social media posts directly from a Notion database using Ayrshare's no-code integrations. - [Google Business Profile: Own The Search Results For Your Business](https://www.ayrshare.com/blog/google-business-profile-own-the-search-results-for-your-business.md): Understand what is a Google Business Profile and why it is important for your business, plus how to post using an API. - [The Free Video Metadata Inspector](https://www.ayrshare.com/blog/the-free-video-metadata-inspector.md): Inspect remote video files easily with our free video metadata inspector tool. Analyze dimensions, codecs, and formats for optimal social media posting. - [Post To Social Media From Your Website Form Using Make & Ayrshare [video]](https://www.ayrshare.com/blog/post-to-social-media-from-your-website-form-using-make-ayrshare-video.md): A no-code video tutorial which explains how you can post to your user's social media accounts using Make and Ayrshare's social API. - [Twitter Bans Link In Bio Pages & Social Media Link Aggregators [updated]](https://www.ayrshare.com/blog/twitter-bans-link-in-bio-pages-social-media-link-aggregators.md): Twitter new policy bans social media aggregators and posting link to other social sites such as Facebook and Instagram. - [How to Enhance Firebase Emulator Logs](https://www.ayrshare.com/blog/how-to-enhance-firebase-emulator-logs.md): Firebase emulator console logging is pretty basic. Enhance the output with color, formatting, and quiet (remove system output). - [LinkedIn API: How to Post and Get Analytics With the LinkedIn API](https://www.ayrshare.com/blog/how-to-post-and-get-analytics-with-the-linkedin-api.md): Discover how the LinkedIn API can help your clients’ businesses by letting you post and get analytics directly from your platform or app on behalf of your clients. - [What is a Link In Bio Page?](https://www.ayrshare.com/blog/what-is-a-link-in-bio-page.md): But what does "link in bio" mean and why has it become such a prevalent tool for social media users? - [HURL: Run and Test HTTP API Requests](https://www.ayrshare.com/blog/hurl-run-and-test-http-api-requests.md): Hurl: run and test API request via the command line. Here is how to use Hurl with real example calling a social media API. - [Twitter API: How to Post and Get Analytics With the Twitter API](https://www.ayrshare.com/blog/twitter-api-how-to-post-and-get-analytics-with-the-twitter-api.md): How to Post and Get Analytics With the Twitter API - [Build A Social Profile Analytics App with Bubble and Ayrshare [video]](https://www.ayrshare.com/blog/build-a-social-profile-analytics-app-with-bubble-and-ayrshare-video.md): A no-code tutorial that explains how to build your own social media profile analytics app with the Bubble web app builder and the Ayrshare social media API. - [What Happened to the Buffer API?](https://www.ayrshare.com/blog/what-happened-to-buffers-api.md): If you want to post to social networks, you need an API What happened to the Buffer API and what are the alternative APIs for social? - [Getting Started with Retool: Build A Social Media Posting App With No-Code [Video]](https://www.ayrshare.com/blog/build-a-social-media-posting-app-with-retool-video.md): In this video we will show how an agency or a marketing team can build their own social media management system using Retool a no-code tool. - [Auto Create Real Estate Social Media Images Via a Template API](https://www.ayrshare.com/blog/automatically-create-real-estate-social-media-images-via-an-api.md): With Ayrshare, you can automatically create great images and post the image to your social media accounts using a social media template API. - [Ayrshare Reinvents Social Media Integrations With New Social Media API Features [Press Release]](https://www.ayrshare.com/blog/ayrshare-reinvents-social-media-integrations-with-new-social-media-api-features-press-release.md): Ayrshare, the leading social media API provider, announced that it has released over 50 new major features, available immediately to the thousands of businesses globally who rely on Ayrshare to power their platform. - [Facebook Reels API: How to Post Facebook Reels Using a Social Media API](https://www.ayrshare.com/blog/facebook-reels-api-how-to-post-fb-reels-using-a-social-media-api.md): How to use the Facebook Reels API to post short form videos. Learn how a social media API can make Reels integration easy. - [Which social media platform is best for attracting B2B clients?](https://www.ayrshare.com/blog/which-social-media-platform-is-best-for-attracting-b2b-clients.md): Let’s help you find the best social media platform for business-to-business (B2B) by weighing the pros and cons of each social media network. - [Get All Your Users Posting To Their Social Accounts on Bubble.io [video]](https://www.ayrshare.com/blog/get-all-your-users-posting-to-their-social-accounts-on-bubble-io-video.md): If you are using the Social API Business Plan or Enterprise Plan, this video shows you how to set up Bubble.io to enable all your users to link their social accounts. - [Automatically Post To Social Media From Airtable](https://www.ayrshare.com/blog/automatically-post-to-social-media-from-airtable.md): A demo of how to use Airtable and two additional no-code tools to build your own social media posting application, Twitter and LinkedIn. - [10 Tips for Using Hashtags Effectively on Facebook, Twitter & Instagram](https://www.ayrshare.com/blog/10-tips-for-using-hashtags-effectively-on-facebook-twitter-instagram.md): Hashtags are an essential tool for social media marketers. Learn 10 tips for using hashtags effectively in this post! - [What is an API? A Comprehensive Beginner’s Guide And Mini Tutorial](https://www.ayrshare.com/blog/what-is-an-api-a-comprehensive-beginners-guide-and-mini-tutorial.md): What is an API and how do you make API calls? Learn the importance of an API and how to use Postman to make API requests. - [Instagram Reels API: How to Post Videos to Reels Using a Social Media API](https://www.ayrshare.com/blog/instagram-reels-api-how-to-post-videos-to-reels-using-a-social-media-api.md): How to post Reels to Instagram using an API. Instagram recently updated their API to allow posting reel videos to Reels. - [How to Sequentially Resolve an Array of Promises in JavaScript](https://www.ayrshare.com/blog/how-to-sequentially-resolve-an-array-of-promises-in-javascript.md): Sometimes you need to process an array of Promises in sequence. Here is how to do it in JavaScript with a for loop. - [Introducing Twitter Notes: Long Form Essays Up to 2,500 Words + Edits](https://www.ayrshare.com/blog/twitter-notes-long-form-essays.md): Twitter has released a new post type for long form writing. Twitter Notes long form written essays allow up to 2,500 words and editing. - [Announcing The Ayrshare WordPress Plugin](https://www.ayrshare.com/blog/announcing-the-ayrshare-wordpress-plugin.md): Announcing the Ayrshare Wordpress plugin for posting to all your social media accounts: Instagram, Facebook, Twitter, and more. - [5 Types of Social Media Content That Convert Traffic Into Customers](https://www.ayrshare.com/blog/5-types-of-social-media-content-that-convert-traffic-into-customers.md): How do you convert traffic into customers? Here are 5 Types of Social Media Content that will do the trick. - [Get Social Media Analytics with the Ayrshare API (Video)](https://www.ayrshare.com/blog/get-social-media-analytics-with-the-ayrshare-api-video.md): In this one minute video see how it easy it is to get the post analytics from all of the social networks: Instagram, TikTok, and Facebook. - [Social Media Demographics Comparison](https://www.ayrshare.com/blog/social-media-demographics-comparison.md): Social media demographics are important when you are putting your marketing strategy. Learn how to approach each social network. - [Best Times & Frequency to Post on Social Media Networks](https://www.ayrshare.com/blog/best-times-frequency-to-post-on-social-media-networks.md): These are the best times to publish on Instagram, Twitter, Facebook and LinkedIn! - [Proven Social Media Content Strategies With Examples from Brands](https://www.ayrshare.com/blog/proven-social-media-content-strategies-with-examples-from-brands.md): Different Approaches to Publishing Social Media Content With Examples from Brands - growing on Instagram, TikTok, and Twitter. - [Top 7 Tips and Tricks For X/Twitter API Posting](https://www.ayrshare.com/blog/top-7-tips-and-tricks-for-twitter-api-posting.md): Top 7 Tips and Tricks For X API Posting. If you are planning to use an API for your connectivity to the social network, then you have to do things a bit differently. - [Press Release: Ayrshare Announces 100 million API Calls as Demand for Social Media Integrations Surges](https://www.ayrshare.com/blog/press-release-ayrshare-announces-100-million-api-calls-as-demand-for-social-media-integrations-surges.md): Ayrshare Announces 100 million API Calls as Demand for Social Media Integrations Surges - [Changelog: What’s New with Facebook Graph API and Marketing API Version 13.0](https://www.ayrshare.com/blog/whats-new-with-facebook-graph-api-version-13-0-and-marketing-api-version-13-0.md): Facebook updated their Graph and Marketing APIs to v13.0, including the Instagram API and WhatsApp API. Here's what's new in the changelogs. - [Twitter Launches “Automated” Label for Bots](https://www.ayrshare.com/blog/twitter-launches-automated-label-for-bots.md): Twitter launches new automation rules requiring bots to add the Automated label. Here is how to enable it on your Twitter account. - [Social Media APIs for Beginners](https://www.ayrshare.com/blog/social-media-apis-for-beginners.md): A plain-language guide to social media APIs: what they are, how OAuth and access tokens work, what each major network’s API allows in 2027, and how to publish to every network with one integration. - [How to Improve Content with Social Media APIs: 3 Tips from Marketing Pros](https://www.ayrshare.com/blog/how-to-improve-content-with-social-media-apis-3-tips-from-marketing-pros.md): How to use Social Media, Analytics, and Marketing APIs, to tap into your social audiences and improve your content. - [How to Automatically Post to Social Media from Your Blog or Newsletter’s RSS Feed](https://www.ayrshare.com/blog/how-to-automatically-post-to-social-media-from-rss-feed.md): How to automatically post your blog or newsletter to your social media accounts, Facebook, Instagram, Twitter, using the RSS Feed. - [YouTube API: How to Upload YouTube Shorts](https://www.ayrshare.com/blog/post-youtube-shorts-with-an-api.md): YouTube Shorts are the latest entry in the short-form video arena. See how to upload a YouTube Shorts video using the YouTube API. #Shorts - [TikTok API: How to Post TikTok Videos Using a Social Media API](https://www.ayrshare.com/blog/tiktok-api-how-to-post-to-tiktok-using-a-social-media-api.md): How to post videos to TikTok using an API. Either share your own videos or on behalf of your users from your platform. - [Instagram Hashtag Guide: The Best Way to Use Hashtags on Instagram](https://www.ayrshare.com/blog/instagram-hashtag-guide.md): Create an Instagram strategy to find the most popular and relevant hashtags so your posts get the most engagement. - [Instagram and Twitter Character Counter Tool](https://www.ayrshare.com/blog/instagram-and-twitter-character-counter-tool.md): Use a character counter tool so you never go over the Instagram caption limit or Twitter's character limits - [How To Share a Podcast on Instagram](https://www.ayrshare.com/blog/how-to-share-a-podcast-on-instagram.md): How do you share a podcast on Instagram? You can’t directly upload an audio file to Instagram stories, but you can create an Audiogram! - [The Definitive Bubble Review: A Flexible No Code App Builder Growing Over 50%](https://www.ayrshare.com/blog/the-definitive-bubble-review-a-flexible-no-code-app-builder-growing-over-50.md): The definitive Bubble.io review. No code app builders, web and mobile apps, are hot and Bubble IO is one of the most popular, growing at 50%. - [Pinterest API Integration on Ayrshare](https://www.ayrshare.com/blog/pinterest-api-integration-on-ayrshare.md): Our Pinterest API integration is available on Ayrshare. You can now share Pins to Pinterest's 454 million active monthly users. - [What is Social Media?](https://www.ayrshare.com/blog/what-is-social-media.md): Social Media has become ingrained in our society, so we look at how to answer what is social media and where did it come from? - [Why You Should Put a CDN Like Cloudflare in Front of Firebase](https://www.ayrshare.com/blog/why-you-should-put-a-cdn-like-cloudflare-in-front-of-firebase.md): There are three good reasons you should consider putting a CDN like Cloudflare in front of Firebase. - [How to Put a CDN in Front of Firebase Cloud Storage](https://www.ayrshare.com/blog/how-to-put-a-cdn-in-front-of-firebase-cloud-storage.md): A work-around to put Firebase Cloud Storage behind a CDN such as Cloudflare to speed up access to your content. - [Top 5 Tips To Optimize Your Instagram Posting in 2021](https://www.ayrshare.com/blog/top-5-tips-to-optimize-your-instagram-posting-in-2021.md): The top 5 tips to optimize your Instagram posts and stories to create the perfect Instagram post for maximum engagement and SEO. - [Ayrshare Launches YouTube, Instagram, Google My Business API Enhancements for Social Media](https://www.ayrshare.com/blog/ayrshare-launches-youtube-instagram-google-my-business-api-enhancements-for-social-media.md): Ayrshare Launches YouTube, Instagram, Google My Business API Enhancements for Social Media - [How Many Hashtags Should I Use On Social Media?](https://www.ayrshare.com/blog/how-many-hashtags-should-i-use-on-social-media.md): Hashtags and their use on social media platforms differ. Here are details on how many hashtags you should use on each. - [A Better White Label Social Media Management Platform](https://www.ayrshare.com/blog/a-better-white-label-social-media-management-platform.md): Using a pre-built white label solution will always end up in some frustration. And you may be saving 30% to 80% by using an API solution. - [What Is Spintax, And Why Is It Bad for SEO?](https://www.ayrshare.com/blog/what-is-spintax-and-why-is-it-bad.md): Spintax spins the syntax of social media posts to make them different. Though it sounds good, the concept doesn’t help your SEO strategy. - [A Firebase Cloud Functions Cold Start Solution](https://www.ayrshare.com/blog/a-firebase-cloud-functions-cold-start-solution.md): Firebase Cloud Functions cold start problem finally has a solution with minimum instances. Your functions will now run fast, but at a price. - [What’s New with Facebook Graph API and Marketing API Version 12.0](https://www.ayrshare.com/blog/whats-new-with-facebook-graph-api-and-marketing-api-version-12-0.md): On September 14th, 2021 Facebook released their latest Graph API version 12.0 and Marketing API version 12.0. Here is what is new. - [When to Use (and Not Use) Ayrshare for Social Media Management](https://www.ayrshare.com/blog/why-you-shouldnt-use-ayrshare.md): When to use and not to use Ayrshare's social media API for managing your users' social media accounts. - [Emailing with Firebase: The Trigger Email Extension](https://www.ayrshare.com/blog/fireabase-email-extension-guide.md): How do you send emails from Firebase. The new Firebase extension Trigger Email makes it easy, and here's how. - [Twitter Launches Spaces API for Live Audio Conversations](https://www.ayrshare.com/blog/twitter-launches-spaces-api-for-live-audio-conversations.md): Twitter has launched their Spaces API for live audio conversations, allowing discovery and lookup integrations for 3rd party apps. - [Build A Social Media Posting Mobile App With No Code](https://www.ayrshare.com/blog/build-a-social-media-posting-mobile-app-with-no-code.md): In this tutorial, we show you how to make your own mobile social media scheduling app using the no-code platform AppGyver and the Ayrshare API. - [How To Create A Social Media Scheduling App With No Code](https://www.ayrshare.com/blog/how-to-create-a-social-media-scheduling-app-with-no-code.md): How to build a no-code web app to create and schedule a post for multiple social media destinations using Bubble.io and Ayrshare's API - [Top 3 Reasons Real Estate Social Media Video Posts Drive Business](https://www.ayrshare.com/blog/top-3-reasons-real-estate-social-media-video-posts-drive-business.md): Posting beautiful and professional videos to social media can help Real Estate agents to find new listings and engage buyers. - [Upptime: An open-source uptime monitor for website and API uptime status](https://www.ayrshare.com/blog/upptime-monitor-status-website-api.md): I'm on the hunt for a system to monitor and show our users the website and API's uptime status - important for any SaaS or API service. I found Upptime. - [Why did Hootsuite Raise Prices 1200%?](https://www.ayrshare.com/blog/why-did-hootsuite-raise-prices-1200.md): Hootsuite just increased their prices 1200% for a lot of their long time users. Why did they do this? - [What Is The Most Popular Social Media Platform in 2021?](https://www.ayrshare.com/blog/what-is-the-most-popular-social-media-platform-in-2021.md): A look at the most popular social media platforms in 2021 including Instagram, Facebook, TikTok, and WhatsApp. - [How We Got 1,000 Users by Engaging Developers](https://www.ayrshare.com/blog/how-we-got-1000-users-by-engaging-developers.md): How do you effectively market to your developer or technical audience? There are three ways to boost your lead generation efforts by 30%. - [Ayrshare Supports Direct Posting to Instagram](https://www.ayrshare.com/blog/ayrshare-supports-direct-posting-to-instagram.md): Direct social media posting to Instagram via Ayrshare's API. Now you can post images or videos directly to Instagram. - [The Future Of Twitter](https://www.ayrshare.com/blog/the-future-of-twitter.md): Twitter is innovating with new community features like Spaces and Ad formats like Carousel and Branded Likes. Learn what is next for Twitter. - [Ayrshare’s YouTube API to Upload Videos](https://www.ayrshare.com/blog/ayrshare-api-posting-videos-to-youtube.md): Want to upload and share a video to YouTube via an API? Ayrshare's YouTube API integration allows you to upload and post a video a thumbnail. - [How to Post Facebook Images as a Carousel](https://www.ayrshare.com/blog/post-a-series-of-facebook-images-as-a-carousel.md): Post a series of Facebook carousel images via Ayrshare's API with the new "carousel" feature. It is easy to setup and post beautiful images. - [Post to Multiple Social Media Accounts from Bubble](https://www.ayrshare.com/blog/post-to-multiple-social-media-accounts-from-bubble.md): How to connect and post to social media networks like Twitter, Instagram, or Facebook from no-code tool Bubble.io. - [Why Your Business Should Have a Telegram Strategy](https://www.ayrshare.com/blog/why-your-business-should-have-a-telegram-strategy.md): Telegram is a popular and global communications platform that helps you grow your brand and be a powerful content marketing channel. - [The Power of Javascript Promise.all()](https://www.ayrshare.com/blog/the-power-of-javascript-promise-all.md): Javascript's Promise.all() is a powerful function that allows you to resolve multiple Promises and return the results. - [Ayrshare Launches the API-First Social Media Management Platform](https://www.ayrshare.com/blog/ayrshare-launches-the-api-first-social-media-management-platform.md): Today we announced the global launch of the API-First Social Media Management Platform. As the first focused API-first solution in the market, we are excited with the great feedback and traction we have with our early users. - [What are Twitter Fleets and How to Use Them](https://www.ayrshare.com/blog/what-are-twitter-fleets-and-how-to-use-them.md): Twitter Fleets are here! They are new. They are exciting. They are innovative. They are...well, kind of a copy of other social networks. - [What’s New with Facebook Graph API 9.0?](https://www.ayrshare.com/blog/whats-new-with-facebook-graph-api-9-0.md): On November 10th, 2020 Facebook released version 9.0 of their Graph API. Here is what is new and important. - [What Is Driving Twitter’s User Growth?](https://www.ayrshare.com/blog/what-is-driving-twitters-user-growth.md): In the most recent quarter ended June 2020, Twitter reported worldwide mDAU of 186 million, which was a 12% increase from the prior quarter. In the last few quarters, the growth of mDAU has been accelerating. This indicates that the strategy Twitter has taken to improve user engagement on the platform has been working. - [Our Firebase Tech Stack](https://www.ayrshare.com/blog/our-firebase-tech-stack.md): How we used Firebase and Firestore to build Ayrshare: the API to automatically publish to your social media networks. - [Automate Your Social Media Strategy](https://www.ayrshare.com/blog/automate-your-social-media-strategy.md): One way to enhance your social media strategy and presence is by automating the creation and publication of your content. - [Automatically Publish Cryptocurrency Prices to Social Media Networks](https://www.ayrshare.com/blog/automatically-publish-cryptocurrency-prices-social-media-networks.md): Publish Cryptocurrency prices to social media networks via an API using Firebase, Coin Gecko, and Ayrshare ## Sitemaps - [Markdown sitemap](https://www.ayrshare.com/sitemap-ai.xml): the authoritative list of this site's Markdown URLs. Those listed there are formed by appending `.md` to the page URL (the home page is `https://www.ayrshare.com/index.md`); URLs not listed there have no Markdown rendition. - [Sitemap](https://www.ayrshare.com/sitemap.xml): every page as canonical HTML. # Connect to the MCP Server Source: https://www.ayrshare.com/docs/additional/mcp-action-connect Connect any MCP client to the Ayrshare MCP Server — transport, endpoint, and authentication. The Ayrshare [MCP Server](/docs/additional/mcp-action-server) lets an AI agent drive the Ayrshare API. This page covers how to connect to it and how authentication works. ## Endpoint and transport The production MCP Server is available at: `https://api.ayrshare.com/mcp` It uses **Streamable HTTP** transport and is **stateless** — there is no session to maintain between calls. ## Supported clients The Action MCP Server authenticates **only** with a static `Authorization: Bearer` request header. That one requirement decides everything below: a client that can set a custom HTTP header on a Streamable HTTP connection can authenticate, and a client that cannot, cannot. Find your client before you start setup. | Client | Can it authenticate? | How, or why not | | - | - | - | | **Claude Code (CLI)** | **Yes** | `claude mcp add --transport http … --header "Authorization: Bearer YOUR_API_KEY"`. This is the recommended path — see [Option B: Any MCP client](#option-b-any-mcp-client) below. | | **Claude Code plugin** | **Yes** | The bundled MCP configuration sends your key as the `Authorization` header. See [Claude Code Plugin](/docs/additional/mcp-claude-code-plugin). | | **Claude Desktop — personal connector** | **No** | A personal custom connector cannot send a static `Authorization` header, and Ayrshare has not verified any third-party bridge that adds one. Use [Claude Code](#option-b-any-mcp-client) instead, or see the organization option below. | | **claude.ai web — personal connector** | **No** | A personal connector cannot send a static `Authorization: Bearer` header, and Ayrshare publishes no OAuth metadata for it to use instead. See [Why personal connectors cannot authenticate](#why-personal-connectors-cannot-authenticate) below. | | **Claude mobile — personal connector** | **No** | Same as claude.ai web: a personal connector has no way to present an Ayrshare API key. | | **Claude Team or Enterprise — organization administrator** | **Beta, administrators only** | Anthropic's `static_headers` request-header option, available in beta to organization administrators in a limited set of organizations. See [Team and Enterprise organizations](#team-and-enterprise-organizations) below. | | **Cursor** | **Yes** | Supported via a `headers` block on the remote server entry — `"Authorization": "Bearer YOUR_API_KEY"` — per [Cursor's MCP documentation](https://cursor.com/docs/context/mcp). | | **VS Code** | **Yes** | Supported via the `headers` property on an `http` MCP server entry, per [VS Code's MCP configuration reference](https://code.visualstudio.com/docs/agents/reference/mcp-configuration). | | **n8n** | **Yes** | Supported via a Bearer credential (or a generic header) on the MCP Client Tool node, per [n8n's MCP Client Tool documentation](https://docs.n8n.io/integrations/builtin/cluster-nodes/sub-nodes/n8n-nodes-langchain.toolmcp/). See the [n8n guide](/docs/additional/mcp-n8n). | | **Any client supporting custom HTTP headers** | **Yes** | Send `Authorization: Bearer YOUR_API_KEY` on every request. That is the server's only authentication requirement, so a client not listed above is still answerable by this rule. | ### Why personal connectors cannot authenticate Ayrshare's Action MCP requires a static `Authorization: Bearer` header. Adding it as a personal custom connector on claude.ai, Claude Desktop, or Claude mobile does not work, because Ayrshare publishes no OAuth metadata and the request-header alternative is a beta limited to approved organizations. On a personal connector, no setting on your side fixes this: there is no field for an `Authorization` header, and no API key, profile, or account setting that enables one. **Ayrshare does not publish OAuth metadata, so the connector's OAuth path has nothing to connect to.** Use a client from the table above instead: [Claude Code](#option-b-any-mcp-client), the [Claude Code plugin](/docs/additional/mcp-claude-code-plugin), or any client that lets you set a custom `Authorization` header. If you are an administrator of an organization in Anthropic's request-header beta, see [Team and Enterprise organizations](#team-and-enterprise-organizations) below. > [!WARNING] > > **If your connector looks healthy but every tool fails.** The connector adds cleanly, reports **healthy**, and lists the **full tool catalog** — and then **every** tool call returns Ayrshare **403 / code 102, "API Key not valid"**. That is this limitation, not a broken key — your client cannot send the static `Authorization: Bearer` header this server requires. `initialize` and `tools/list` are reachable before authentication (see [Authentication](#authentication) below), so nothing fails until the first tool call, when it becomes clear no credential was ever sent. > > **Client capability, or a genuinely bad key?** They return the identical error, so use the signature to tell them apart. A client-capability failure means the connector is healthy **and** every tool fails, while the same key works elsewhere. A bad key produces the same error but is reproducible outside the connector — it fails on a REST call too. For the error semantics themselves, see [Authentication errors](#authentication-errors) below. ### Team and Enterprise organizations Anthropic's `static_headers` mechanism lets a custom connector send a fixed credential as a request header instead of using OAuth. It is **available in beta to organization administrators in a limited set of organizations**, and it is **not** available to individual users adding a personal connector. If your organization has access, the administrator must enter the header value as `Bearer ` followed by your Ayrshare API key, including the space — Claude sends the value exactly as entered and does not add the scheme for you. For current availability and setup, see [Anthropic's connector authentication documentation](https://claude.com/docs/connectors/building/authentication). > [!WARNING] > > **The key is shared by the whole organization.** Anthropic's request-header credential is entered once by an administrator and used by every member of the organization, so all of them act as the single Ayrshare account that key belongs to — with access to every [User Profile](/docs/multiple-users/manage-user-profiles) under it on a Business key, and no per-user attribution in your Ayrshare history or analytics. Treat it as a shared service credential, not as individual access. ## Authentication Authentication is enforced by the same Ayrshare API chain that powers the REST API. ### Precedence: argument wins over header When a tool call includes a `profileKey` argument **and** the connection has a `Profile-Key` header, the **per-call `profileKey` argument wins**. The header is used only when no valid argument is provided. One exception: on `get_platform_history` and `get_social_network_analytics`, an X/Twitter `userId`/`userName` lookup must use the account API key only; supplying a `profileKey` argument or `Profile-Key` header there returns Error 400. > [!NOTE] > > The `initialize` and `tools/list` MCP methods are reachable **before** authentication — they return metadata only and execute nothing. **Every tool call is authenticated.** ### Authentication errors An unauthenticated or invalid key on a tool call returns an Ayrshare **error 403 / code 102** with message **"API Key not valid"**. Per the MCP spec, tool-execution errors are returned in-band: the tool result carries `isError: true` and this message, while the MCP transport itself responds **HTTP 200**. (The `403`/`102` are Ayrshare's application error, not the transport status.) ## Connect ### Option A: Claude Code plugin If you use Claude Code, install the Ayrshare plugin. It bundles the MCP Server configuration, a setup command, agents, skills, and a confirmation hook. See the [Claude Code Plugin](/docs/additional/mcp-claude-code-plugin) page for the full install steps. ### Option B: Any MCP client For any MCP client that supports Streamable HTTP **and lets you set a custom request header** — see [Supported clients](#supported-clients) above — register the server directly. In Claude Code: ```bash theme={"system"} claude mcp add --transport http ayrshare https://api.ayrshare.com/mcp --header "Authorization: Bearer YOUR_API_KEY" ``` To target a sub-profile on every call, add the optional `Profile-Key` header: ```bash theme={"system"} claude mcp add --transport http ayrshare https://api.ayrshare.com/mcp \ --header "Authorization: Bearer YOUR_API_KEY" \ --header "Profile-Key: YOUR_PROFILE_KEY" ``` > [!WARNING] > > The MCP connection initializes at session start. **Restart your MCP client** after installing the server or changing your key so the new configuration takes effect. ## X/Twitter BYO credentials Since March 31, 2026, X/Twitter operations through Ayrshare require your own OAuth 1.0a credentials. When a tool call targets X/Twitter, forward these two headers on the connection alongside your `Authorization` (and optional `Profile-Key`) headers: | Header | Description | | - | - | | `X-Twitter-OAuth1-Api-Key` | Your OAuth 1.0a API Key (Consumer Key) | | `X-Twitter-OAuth1-Api-Secret` | Your OAuth 1.0a API Key Secret (Consumer Secret) | These are the same headers used by the REST API — one OAuth 1.0a key pair per Ayrshare account, sent on every X-targeting request (the same pair applies to all sub-profiles). Ayrshare does not use OAuth 2.0 here. See the [API Overview](/docs/apis/overview#xtwitter-byo-credentials) and the [X BYO Key Setup Guide](/docs/dashboard/connect-social-accounts/x-twitter-byo-keys) for setup, policy, and troubleshooting. > [!WARNING] > > Without these headers, an X/Twitter tool call returns error `419` (`x_credentials_required`). ### Connect with the BYO headers To set up X/Twitter BYO credentials from the start, add the server with both OAuth 1.0a headers alongside your `Authorization` header (include `Profile-Key` too if you target a sub-profile on every call): ```bash theme={"system"} claude mcp add --transport http ayrshare https://api.ayrshare.com/mcp \ --header "Authorization: Bearer YOUR_API_KEY" \ --header "X-Twitter-OAuth1-Api-Key: YOUR_TWITTER_CONSUMER_KEY" \ --header "X-Twitter-OAuth1-Api-Secret: YOUR_TWITTER_CONSUMER_SECRET" ``` ### Add the BYO headers to an existing connection Connection headers are fixed when the server is added, so if you already connected without the BYO headers, remove the server and re-add it with the full set (re-include any headers you were already using, such as `Profile-Key`): ```bash theme={"system"} claude mcp remove ayrshare claude mcp add --transport http ayrshare https://api.ayrshare.com/mcp \ --header "Authorization: Bearer YOUR_API_KEY" \ --header "X-Twitter-OAuth1-Api-Key: YOUR_TWITTER_CONSUMER_KEY" \ --header "X-Twitter-OAuth1-Api-Secret: YOUR_TWITTER_CONSUMER_SECRET" ``` Restart your MCP client after changing headers so the new configuration takes effect. Using the Claude Code plugin instead of a raw connection? See [Claude Code Plugin → X/Twitter BYO credentials](/docs/additional/mcp-claude-code-plugin#xtwitter-byo-credentials). ## Next steps > [card] [Tool Catalog](/docs/additional/mcp-action-tools) > > The 33 tools grouped by domain, with scope and purpose. > [card] [Claude Code Plugin](/docs/additional/mcp-claude-code-plugin) > > Install the Ayrshare plugin for Claude Code. # Ayrshare MCP Server: Drive the API with AI Agents Source: https://www.ayrshare.com/docs/additional/mcp-action-server Let AI agents drive the Ayrshare API through the Model Context Protocol (MCP) Server. > [!NOTE] > > Looking for the read-only docs-search MCP? See [Documentation MCP](/docs/additional/mcp-server). **This page documents the Action MCP** that lets an AI agent drive the Ayrshare API. The Ayrshare **MCP Server** lets AI agents drive the Ayrshare API through [Model Context Protocol (MCP)](https://modelcontextprotocol.io) tools. Instead of writing REST calls by hand, an agent connects to the MCP Server and calls Ayrshare tools to publish posts, fetch history and analytics, manage comments and direct messages, create profiles, and more. ## How it works Each MCP tool call is dispatched in-process through the same Ayrshare API chain that powers the REST API: Because tools run through the real API chain, the MCP Server returns the same data and enforces the same rules as the REST API. There is no separate behavior to learn or maintain. ## Who it's for > [!NOTE] > > Working with profiles and sub-profiles requires a **Business plan** (or Enterprise). See the [Business Plan Overview](/docs/multiple-users/business-plan-overview). ## Endpoint and transport The production MCP Server is available at: `https://api.ayrshare.com/mcp` It uses **Streamable HTTP** transport and is **stateless** — there is no session to maintain between calls. See [Connect & Setup](/docs/additional/mcp-action-connect) for the full connection and authentication details. ## Relationship to the REST API and Documentation MCP ## Next steps > [card] [Connect & Setup](/docs/additional/mcp-action-connect) > > Transport, endpoint, and authentication for any MCP client. > [card] [Tool Catalog](/docs/additional/mcp-action-tools) > > The 33 tools grouped by domain, with scope and purpose. > [card] [Claude Code Plugin](/docs/additional/mcp-claude-code-plugin) > > Install the Ayrshare plugin for Claude Code. > [card] [Client Onboarding](/docs/additional/mcp-client-onboarding) > > Mint social-linking URLs for client sub-profiles. # MCP Tool Catalog Source: https://www.ayrshare.com/docs/additional/mcp-action-tools Lists each Ayrshare MCP Server tool by domain, from posts and analytics to webhooks and ads, and notes if it takes a profileKey or runs at account scope. The Ayrshare [MCP Server](/docs/additional/mcp-action-server) exposes **33 tools**, grouped below by domain. Each tool is dispatched in-process through the real Ayrshare API chain. ## Scope Every tool is either **Profile** or **Account** scope: The six tools that take **no** `profileKey` argument are: `create_profile`, `list_profiles`, `explain_error`, `validate_media`, `generate_post`, and `recommend_hashtags`. All other tools accept a `profileKey`; `generate_jwt_social_linking_url` **requires** one. Note: `recommend_hashtags` takes no `profileKey` argument but still reads the connection's `Profile-Key` header to pick whose linked TikTok account it draws on, so its results are profile-specific. When both a per-call `profileKey` argument and a `Profile-Key` header are present, the argument wins. See [Connect & Setup](/docs/additional/mcp-action-connect#precedence-argument-wins-over-header) for the precedence rules. ## Posts | Tool | Scope | Purpose | | - | - | - | | `create_post` | Profile | Publish to one or more linked platforms. | | `validate_post` | Profile | Dry-run validation, same input as `create_post`. | | `get_post` | Profile | Fetch a post by Ayrshare Post ID. | | `update_post` | Profile | Update a scheduled or awaiting-approval post. | | `retry_post` | Profile | Re-submit a failed post. | ## History | Tool | Scope | Purpose | | - | - | - | | `get_post_history` | Profile | Posts sent via Ayrshare. | | `get_platform_history` | Profile | Posts and analytics direct from the network, including non-Ayrshare posts. For an X/Twitter `userId`/`userName` lookup, use the account API key only; a `profileKey` argument or `Profile-Key` header there returns Error 400. | ## Analytics | Tool | Scope | Purpose | | - | - | - | | `get_post_analytics` | Profile | Per-post metrics by Ayrshare Post ID. | | `get_post_analytics_by_social_id` | Profile | Per-post metrics by native Social Post ID. | | `get_social_network_analytics` | Profile | Account-wide analytics and demographics for a connected social account. For an X/Twitter `userId`/`userName` lookup, use the account API key only; a `profileKey` argument or `Profile-Key` header there returns Error 400. | ## Comments | Tool | Scope | Purpose | | - | - | - | | `get_comments` | Profile | Read comments on a post. | | `add_comment` | Profile | Top-level comment. | | `reply_comment` | Profile | Reply to a comment. | ## Messages / DMs | Tool | Scope | Purpose | | - | - | - | | `get_messages` | Profile | Fetch DMs for a platform. | | `send_message` | Profile | Send a DM. | | `get_auto_response` | Profile | Read DM auto-response settings. | | `set_auto_response` | Profile | Configure DM auto-response. | ## Profiles | Tool | Scope | Purpose | | - | - | - | | `list_profiles` | Account | List sub-profiles. | | `create_profile` | Account | Create a sub-profile. | | `generate_jwt_social_linking_url` | Profile | Mint a JWT social-linking URL (requires `profileKey`). | ## Media | Tool | Scope | Purpose | | - | - | - | | `validate_media` | Account | HEAD-check a media URL is reachable; returns `contentType`. | ## Generate | Tool | Scope | Purpose | | - | - | - | | `generate_post` | Account | AI-draft post copy. | | `recommend_hashtags` | Account | Hashtag suggestions from TikTok view-count data. Uses the linked TikTok account on the profile set by the `Profile-Key` header; takes no per-call `profileKey`. | ## Webhooks | Tool | Scope | Purpose | | - | - | - | | `register_webhook` | Profile | Register a webhook. | | `unregister_webhook` | Profile | Unregister a webhook. | | `list_webhooks` | Profile | List registered webhooks. | ## Ads Ads tools need the Ads add-on. Without it they return `Error 403: Ads is not enabled. Please subscribe to Ads in the Account page. (code 399)`. Each tool takes a `platform` argument: `facebook` or `instagram`. | Tool | Scope | Purpose | | - | - | - | | `list_ad_accounts` | Profile | List the ad accounts the linked account can spend from. | | `search_ad_targeting` | Profile | Search targeting options: `interests`, `regions` or `cities`. | | `list_ads` | Profile | List boosted ads by campaign, account, ad or post. | | `update_ad` | Profile | Pause, resume or retire an ad. The status is also set on its ad set and campaign, so other ads in them change too, except a campaign or ad set the boost was attached to with `attachTo`; the ad runs only while both are active. `status` takes `ACTIVE`, `PAUSED`, `DELETED` or `ARCHIVED`, all upper case or all lower case (`paused` works, `Paused` does not). Changes real ad spend. | | `get_ad_history` | Profile | Spend and performance history for boosted ads. | | `boost_post` | Profile | Boost a published post as an ad. Takes `postId` or `socialPostId`, `accountId` from `list_ad_accounts`, a `goal` and a `budget` (leave out `budget` and `targeting` with `attachTo.adSetId`). Settings only one platform has go under `platformOptions`; on Facebook and Instagram, `platformOptions..adName` and `.bidAmount` are required, except `.bidAmount` with `attachTo.adSetId`. Spends real money. Unless set otherwise, the ad is active, starts now and has no end date. Send an `idempotencyKey` and reuse it, with the same arguments, on a retry. The retry then returns the first result instead of creating a second ad. | ## Errors | Tool | Scope | Purpose | | - | - | - | | `explain_error` | Account | Decode an Ayrshare error code into cause, classification, and fix. | > [!NOTE] > > This catalog is verified against the live server's `tools/list`. More tools are planned (e.g. Links tools). If in doubt, query `tools/list` for the authoritative set. The catalog is maintained by the MCP server team. # Claude Code Plugin Source: https://www.ayrshare.com/docs/additional/mcp-claude-code-plugin Install the Ayrshare plugin in Claude Code, set your API key and X/Twitter BYO credentials, and review its agents, skills, and confirmation hook. The Ayrshare plugin for Claude Code bundles the [MCP Server](/docs/additional/mcp-action-server) configuration, a setup command, agents, skills, and a safety hook. With it installed, you can publish posts, fetch history and analytics, manage profiles, and more — directly from Claude Code. Before anything goes live, the agent validates each post against the target network's rules and asks you to confirm, catching avoidable rejections up front. Post history is available for matching a brand's voice, and analytics can be read back to inform the next post. The plugin is open source. See the repository for internals: [ayrshare/ayrshare-social-media-api-claude-plugin](https://github.com/ayrshare/ayrshare-social-media-api-claude-plugin). ## Install **1. Add the marketplace (one time)** ```bash theme={"system"} claude plugin marketplace add ayrshare/ayrshare-social-media-api-claude-plugin ``` **2. Install the plugin** ```bash theme={"system"} claude plugin install ayrshare@ayrshare ``` ### Install scopes | Scope | Where it applies | | - | - | | Global (default) | Every project on this machine. | | `--scope local` | Current project, not committed to git. | | `--scope project` | Current project, committed and shared with your team. | > [!NOTE] > > Claude Code's CLI calls the global scope `--scope user`. ## Credentials Provide your Ayrshare API key in either of these ways: The plugin's bundled `.mcp.json` already declares the `Authorization` header plus the optional `Profile-Key` and X BYOK headers, each with an empty default (`${VAR:-}`), so you enable a header simply by setting its environment variable (Claude Code substitutes the values at startup). To target a sub-profile on every call, set `AYRSHARE_PROFILE_KEY` (via `/ayrshare:setup` or your `settings.json` `env` block) and restart Claude Code; the bundled `Profile-Key` header carries it, and stays empty (treated as not provided) when unset. To target a sub-profile for a single call instead, pass a `profileKey` tool argument (no config change needed). You do not need to edit the bundled `.mcp.json`, and you should not, because `claude plugin update` overwrites it. > [!WARNING] > > The MCP server initializes at session start. **Restart Claude Code** after installing the plugin or changing your key. ### X/Twitter BYO credentials Posting to X/Twitter requires your own X Developer App credentials (the X BYO-key mandate, effective March 31, 2026; see the [API Overview](/docs/apis/overview#xtwitter-byo-credentials)). The bundled `.mcp.json` already declares both X BYOK headers with empty defaults, alongside `Authorization` and `Profile-Key`: ```json theme={"system"} "headers": { "Authorization": "Bearer ${AYRSHARE_API_KEY}", "Profile-Key": "${AYRSHARE_PROFILE_KEY:-}", "X-Twitter-OAuth1-Api-Key": "${X_TWITTER_OAUTH1_API_KEY:-}", "X-Twitter-OAuth1-Api-Secret": "${X_TWITTER_OAUTH1_API_SECRET:-}" } ``` So you only set the matching environment variables (`X_TWITTER_OAUTH1_API_KEY`, `X_TWITTER_OAUTH1_API_SECRET`) via `/ayrshare:setup` or your `settings.json` `env`, then **restart Claude Code**; you do not edit the bundled `.mcp.json` (it is overwritten on `claude plugin update`). Set **both** or neither: with neither set an X/Twitter call returns error `419` (`x_credentials_required`); with only one set it returns error `400`. This is one key pair per Ayrshare account, sent on every X-targeting request (the same pair for all sub-profiles). > [!NOTE] > > Prefer a raw connection over the plugin? The same BYO headers can be passed via `claude mcp add --header` — see [Connect & Setup → X/Twitter BYO credentials](/docs/additional/mcp-action-connect#xtwitter-byo-credentials). ## What it ships ### Command ### Agents ### Skills | Skill | Purpose | | - | - | | `getting-started` | Orient and connect for first-time use. | | `post` | Create, validate, update, and retry posts. | | `history` | Retrieve post and platform history. | | `analytics` | Pull per-post and account-level analytics. | | `comments` | Read, add, and reply to comments. | | `messages` | Read and send direct messages and configure auto-responses. | | `profiles` | Create and list sub-profiles. | | `media` | Validate that media URLs are reachable. | | `generate` | AI-draft post copy and recommend hashtags. | | `webhooks` | Register, unregister, and list webhooks. | | `errors` | Decode Ayrshare error codes. | | `draft-in-brand-voice` | Draft post copy in a consistent brand voice. | | `plan-and-schedule-campaign` | Plan and schedule a multi-post campaign. | ### Confirmation hook A `PreToolUse` hook asks for confirmation before any action with a live side effect. If you decline, the tool does not run and nothing is sent to Ayrshare. It covers exactly these 12 tools: > [!NOTE] > > The prompt needs plugin version 0.2.2 or later. Earlier versions of the plugin did not show it. To update, run `claude plugin marketplace update ayrshare`, then `claude plugin update ayrshare@ayrshare`, and restart Claude Code. ## FAQ > [accordion] Does the plugin validate posts before publishing? > > Yes. The plugin runs `validate_post` to dry-run your content against each network's rules — length, format, and media requirements — before it publishes, so you catch avoidable rejections up front. > [accordion] Does it ask before posting to my live accounts? > > Yes. A `PreToolUse` confirmation hook prompts you to approve before any action with a live side effect: `create_post`, `update_post`, `retry_post`, `add_comment`, `reply_comment`, `send_message`, `set_auto_response`, `create_profile`, `update_ad`, `boost_post`, `register_webhook`, and `unregister_webhook`. It needs plugin version 0.2.2 or later. > [accordion] Can the plugin match my brand's voice? > > Yes. The `draft-in-brand-voice` skill reads your post history to draft new copy in a consistent brand voice. It produces drafts only — nothing publishes without your confirmation. > [accordion] Can I read analytics to inform the next post? > > Yes. The analytics tools return per-post and account-level metrics, so you can read results back and let them guide your next post. > [accordion] What do I need to use the plugin? > > An Ayrshare API key (a Business plan key for profiles and sub-profiles) and Claude Code. Configure the key with `/ayrshare:setup` or the `AYRSHARE_API_KEY` environment variable, then restart Claude Code. > [accordion] Does posting to X/Twitter need extra setup? > > Yes. X/Twitter requires your own OAuth 1.0a credentials, added as two connection headers (`X-Twitter-OAuth1-Api-Key` and `X-Twitter-OAuth1-Api-Secret`). See [X/Twitter BYO credentials](#xtwitter-byo-credentials) above. ## Next steps > [card] [Connect & Setup](/docs/additional/mcp-action-connect) > > Authentication, profile targeting, and BYO credentials. > [card] [Tool Catalog](/docs/additional/mcp-action-tools) > > The 33 tools grouped by domain, with scope and purpose. # Onboard Client Social Accounts Source: https://www.ayrshare.com/docs/additional/mcp-client-onboarding Use the MCP Server to onboard clients' social accounts under sub-profiles with a JWT social-linking URL. Integrators on a Business or Enterprise plan can onboard their clients' social accounts under **sub-profiles**. Using the [MCP Server](/docs/additional/mcp-action-server), an agent creates a sub-profile, targets it, and mints a single sign-on linking URL the client opens to connect their accounts. ## Flow **1. Create a sub-profile** Call `create_profile` (account-level — no `profileKey`). It returns a **profile key** for the new sub-profile. **2. Target the sub-profile** Use the returned profile key on subsequent calls — either via the `Profile-Key` header on the connection or as a per-call `profileKey` argument. See [Connect & Setup](/docs/additional/mcp-action-connect#precedence-argument-wins-over-header) for precedence. **3. Mint a social-linking URL** Call `generate_jwt_social_linking_url` to mint a single sign-on linking URL. The JWT signing key and onboarding domain are derived **server-side** — the caller supplies **no** `X-Ayrshare-*` headers and no private key or domain. **4. Client links their accounts** The client opens the URL and authorizes (OAuths) their social accounts. The connected accounts are linked to the sub-profile. ## Requirements > [!WARNING] > > `generate_jwt_social_linking_url` **requires** a `profileKey`. If none resolves, the call returns **400** with the message **"Profile Key is required…"**. > > A **social-linking domain must be provisioned** for your account. This is available on the **Business** and **Enterprise** plans. ### Error states | Condition | Response | | - | - | | No provisioned domain | **400** — "No social-linking domain is provisioned for this account (available on Business/Enterprise plans)." | | No signing key | **400** — "No signing key found for this account's social-linking domain." | ## Next steps > [card] [Connect & Setup](/docs/additional/mcp-action-connect) > > Authentication and profile targeting. > [card] [Tool Catalog](/docs/additional/mcp-action-tools) > > The 33 tools grouped by domain, with scope and purpose. # n8n Source: https://www.ayrshare.com/docs/additional/mcp-n8n Connect n8n's AI Agent to the Ayrshare MCP Server to publish, schedule, and analyze across 13 social networks, with no per-platform API code. n8n is where you wire your tools together. Ayrshare is the one API that publishes to Facebook, Instagram, LinkedIn, YouTube, TikTok, Pinterest, Reddit, Threads, Bluesky, Telegram, Google Business Profile, Snapchat, and X in a single call. (WhatsApp is messaging-only on Ayrshare, so it is not a publishing target. Use the [Messaging API](/docs/apis/messages/overview) for WhatsApp conversations.) Connect them with the [Ayrshare MCP Server](/docs/additional/mcp-action-server) and your n8n AI Agent can run the whole loop on its own: draft a post, validate it against each network's rules, publish or schedule it, then read the analytics back. ![The n8n starter workflow on the canvas: a chat trigger into an AI Agent, with an Anthropic chat model and the Ayrshare MCP tool node attached below, plus setup and usage sticky notes.](https://mintcdn.com/ayrshare-docs/JaXzE30k4rBVuod9/images/packages-guides/n8n-mcp-workflow.webp?fit=max\&auto=format\&n=JaXzE30k4rBVuod9\&q=85\&s=faea4a81aff5120a58b47b609daefad6) Because the MCP Server is a hosted Streamable HTTP endpoint, you connect it with n8n's built-in **MCP Client Tool** node. There is nothing to host, no custom code, and no community node to install. You point one node at one URL and your agent gets all 33 social tools. > [card] [Download the n8n starter workflow](/docs/files/ayrshare-n8n-mcp-workflow.json) > > The workflow shown above, ready to import: Chat Trigger, AI Agent, an Anthropic chat model, and the Ayrshare MCP node pre-wired with the validate-first system prompt. To use it: in n8n, **Workflows → Import from file**, select the JSON, then create the two credentials it expects (a **Bearer Auth** credential holding your Ayrshare key, and an **Anthropic API** credential). The two warning badges clear once the credentials are attached. Self-hosted instances need outbound access to `api.ayrshare.com` and your model provider. This guide covers the MCP-first path end to end. If you would rather build a fixed, non-agent workflow, there is a short [REST fallback](#rest-fallback-no-agent) at the end. ## Why MCP instead of writing API calls You can call Ayrshare's REST API directly from an HTTP Request node, which is fine for fixed, predictable workflows. The MCP Server earns its place when an LLM is in the loop: ## How it connects The **AI Agent** node is the brain. The **MCP Client Tool** sub-node attaches to it, connects to the Ayrshare MCP Server at `https://api.ayrshare.com/mcp`, discovers the available tools, and exposes them to the agent. When the agent acts, the node dispatches the call to Ayrshare, which publishes to the networks. ``` Trigger -> AI Agent (+ Chat Model) -> MCP Client Tool -> Ayrshare MCP Server -> 13 networks ``` For the endpoint, transport, and authentication details, see [Connect & Setup](/docs/additional/mcp-action-connect). For the full tool list, see the [Tool Catalog](/docs/additional/mcp-action-tools). ## Prerequisites ## Set up the MCP Client Tool node **1. Add an AI Agent node** Open or create a workflow and add an **AI Agent** node (under the *Advanced AI* nodes). **2. Attach the MCP Client Tool node** On the AI Agent node, click the **Tool** connector and add an **MCP Client Tool** node. Configure it: | Field | Value | | - | - | | **Endpoint** | `https://api.ayrshare.com/mcp` | | **Server Transport** | `HTTP Streamable` | | **Authentication** | `Bearer Auth` | | **Tools to Include** | `All` (or `Selected` to expose only specific tools) | **3. Add your Ayrshare key as a Bearer credential** For **Credential**, create a new **Bearer Auth** credential and paste your Ayrshare API key as the token. n8n sends it as `Authorization: Bearer YOUR_API_KEY`. When you save, n8n connects and lists the 33 tools. **4. Wire up the chat model and system prompt** Attach a **Chat Model** sub-node and select your model credential. Give the agent a system prompt that sets the rules, for example: > You are a social media assistant with access to Ayrshare tools. Before publishing anything, always call `validate_post` first and report any issues. Only call `create_post` after validation passes. Default to the platforms the user names; if none are named, ask. Never invent media URLs; only use URLs the user provides, and confirm them with `validate_media` when in doubt. **5. Add a trigger** For testing, the **Manual Trigger** or **Chat Trigger** is easiest. For production, use whatever kicks off the workflow (schedule, webhook, form, new row in a sheet). > [!NOTE] > > **Use HTTP Streamable, not SSE.** The Ayrshare MCP Server uses the modern Streamable HTTP transport and is stateless. n8n's SSE option is deprecated, and the server rejects the SSE stream. Always choose **HTTP Streamable**. ## The tool surface Your agent sees all 33 tools through the one MCP node. You rarely call them by name; you describe intent and the agent selects. The domains are Posts, History, Analytics, Comments, Messages, Profiles, Media, Generate, Webhooks, Ads, and Errors. For the complete list with each tool's purpose and scope, see the [Tool Catalog](/docs/additional/mcp-action-tools). Two stand out for safety: **`validate_post`** dry-runs a post with the same input as `create_post` but publishes nothing, and **`explain_error`** turns any Ayrshare error code into a plain-English cause and fix, so the agent can self-diagnose. ## Example 1: draft, validate, and publish from a chat message The "hello world" of this integration. Trigger the workflow with a chat message or form, and let the agent do the rest. What the agent does on its own: it calls `validate_post` with your copy and both platforms; if anything breaks a network's rules it tells you instead of failing silently; once validation passes it calls `create_post` and returns the live post URLs. Because validation runs first, you find out about a problem before anything is public. ## Example 2: auto-publish new content across your channels Turn a content source into multi-network posts without touching it. You can lean on `recommend_hashtags` for data-driven tags and `generate_post` if you want Ayrshare to draft the copy rather than your chat model. ## Example 3: weekly analytics digest Run the loop in reverse: read performance and report it. A note on cadence: most posts get the bulk of their engagement in the first 24 hours, and metrics do not update by the second. A daily or weekly schedule is plenty. Do not poll analytics every few minutes or you will hit rate limits for no new data. ## Acting on behalf of clients (multi-tenant) If you manage social for multiple clients, Ayrshare's profiles let one account post to many separate sets of linked accounts. On a Business or Enterprise plan you have two ways to target a client from n8n: To onboard a new client, the agent can call `create_profile` and then `generate_jwt_social_linking_url` to mint a hosted page where the client links their own accounts. No credentials pass through your workflow. ## Posting to X/Twitter > [!NOTE] > > **Targeting X/Twitter?** Starting March 31, 2026, all X operations require your own API credentials. After linking X via OAuth, include these 2 headers in your request: > > `X-Twitter-OAuth1-Api-Key` — Your API Key (Consumer Key) > > `X-Twitter-OAuth1-Api-Secret` — Your API Key Secret (Consumer Secret) > > **One-time setup per Ayrshare account.** You create one X Developer App and reuse the same API Key and Secret across every sub-profile / end-user you link. You do *not* create a new app per customer. > > Not linked yet? See the [full setup guide](/docs/dashboard/connect-social-accounts/x-twitter-byo-keys) to connect your X account. > > Your keys are never logged or stored by Ayrshare. In n8n, switch the MCP Client Tool node's **Authentication** to **Multiple Headers** and add the two `X-Twitter-OAuth1-*` headers alongside `Authorization`. It is a one-time setup per Ayrshare account, and the same key pair applies to every profile. For everything else (Facebook, Instagram, LinkedIn, YouTube, TikTok, and the rest) no extra headers are needed. See [Connect & Setup → X/Twitter BYO credentials](/docs/additional/mcp-action-connect#xtwitter-byo-credentials). ## Keep a human in the loop The MCP tools make it trivial for an agent to publish, which is exactly why you should gate it. Two cheap safeguards: ## REST fallback (no agent) If you want a fixed, deterministic workflow with no LLM, skip MCP and call the REST API with an **HTTP Request** node: ```json theme={"system"} { "post": "Excited to announce our new feature!", "platforms": ["facebook", "linkedin", "instagram"], "mediaUrls": ["https://example.com/image.jpg"], "scheduleDate": "2026-07-01T10:00:00Z" } ``` Use the API host `api.ayrshare.com`, not the dashboard host `app.ayrshare.com`. (`app.ayrshare.com` will accept API calls, but it is not the documented endpoint and can return intermittent [502/504 gateway errors](/docs/help-center/technical-support/response-bad-gateway-502-or-504-error), so always use `api.ayrshare.com`.) The same pattern works for the other [REST endpoints](/docs/apis/overview). This is the right tool when the workflow is fully predictable and you do not need an agent deciding anything. ## Troubleshooting | Symptom | Likely cause | Fix | | - | - | - | | Tool call returns `403` / code `102`, "API Key not valid" | Bad or missing API key | Check the Bearer Auth credential holds your exact Ayrshare key. Re-save the node after changing it. | | n8n cannot connect to the MCP server | Wrong transport or endpoint | Endpoint must be `https://api.ayrshare.com/mcp`; transport must be **HTTP Streamable**, not SSE. | | X/Twitter post fails with error `419` | Missing X BYO credentials | Add the two `X-Twitter-OAuth1-*` headers via Multiple Headers auth. | | Post fails for one platform only | That network is not linked, or a required field is missing | Link it in the dashboard; add `title` for YouTube, `title` + `subreddit` for Reddit, `mediaUrls` for Instagram. | | Instagram post rejected | No media | Instagram requires `mediaUrls` for most post types. Have the agent confirm with `validate_media`. | | Media attached as a file or binary does not appear | MCP is JSON-only | Reference media by public `mediaUrls` URL; the MCP path does not accept file uploads. Confirm the URL with `validate_media`. | | Calls work, but act on the wrong client | Profile targeting | Set a `Profile-Key` header (per connection) or `profileKey` argument (per call); the argument wins if both are set. | | `429 Too Many Requests` | Polling too often | Reduce analytics and comment polling frequency; metrics do not update by the second. | | Config change did not take effect | MCP connection initializes at session start | Re-save the node or restart the workflow after changing the key or headers. | You can also expose the `explain_error` tool to the agent so it decodes any Ayrshare error code into a cause and fix on its own. ## Next steps > [card] [Connect & Setup](/docs/additional/mcp-action-connect) > > Endpoint, transport, authentication, profile targeting, and BYO credentials. > [card] [Tool Catalog](/docs/additional/mcp-action-tools) > > The 33 tools grouped by domain, with scope and purpose. > [card] [MCP Server](/docs/additional/mcp-action-server) > > What the MCP Server is and how it maps to the Ayrshare API. > [card] [Claude Code Plugin](/docs/additional/mcp-claude-code-plugin) > > The same server, packaged for Claude Code. # Documentation MCP Source: https://www.ayrshare.com/docs/additional/mcp-server Connect the read-only Ayrshare Documentation MCP server to your AI agent to search Ayrshare's API documentation > [!NOTE] > > This is the read-only **Documentation MCP** (it searches Ayrshare's API docs). To let an AI agent **drive** the Ayrshare API, see the [MCP Server](/docs/additional/mcp-action-server) (Action MCP). The Model Context Protocol (MCP) is an open standard that enables applications to share context and tools with Large Language Models (LLMs). By connecting the Ayrshare API documentation MCP server to your AI development tools like Cursor or Claude Desktop, you can give your AI agent direct access to Ayrshare's documentation. This integration allows your AI agent to: This means instead of manually looking up API details, your AI assistant can reference the documentation directly and help you implement Ayrshare functionality more efficiently. ## How to Get Started Ayrshare's MCP server is available at `https://www.ayrshare.com/docs/mcp`. ### Claude Desktop 1. Navigate to the Connectors page in the Claude settings. 2. Select Add custom connector. 3. Add `Ayrshare MCP` or any name you prefer as your MCP server name and `https://www.ayrshare.com/docs/mcp` as your MCP server URL. 4. Select Add. 5. When using Claude, select the attachments button (the plus icon). 6. Select your MCP server. > [!WARNING] > > These steps work because the Documentation MCP requires no authentication. They do not carry over to the Action MCP: it requires a static `Authorization: Bearer` header, which a personal custom connector cannot send, and Ayrshare publishes no OAuth metadata for the connector to use instead. Administrators of organizations in Anthropic's request-header beta have a separate option. See [Supported clients](/docs/additional/mcp-action-connect#supported-clients) for the clients that can authenticate to the Action MCP. ### Claude Code ```bash theme={"system"} claude mcp add --transport http ayrshare https://www.ayrshare.com/docs/mcp ``` ### VS Code [![Install in VS Code](https://img.shields.io/badge/VS_Code-Install_Server-0098FF?style=for-the-badge\&logo=visual-studio-code\&logoColor=white)](https://insiders.vscode.dev/redirect?url=vscode:mcp/install?%7B%22type%22%3A%22http%22%2C%22name%22%3A%22Ayrshare-MCP%22%2C%22url%22%3A%22https%3A%2F%2Fwww.ayrshare.com%2Fdocs%2Fmcp%22%7D) 1. Create a .vscode/mcp.json file. 2. In mcp.json, configure your server: ```json theme={"system"} { "servers": { "Ayrshare MCP": { "type": "http", "url": "https://www.ayrshare.com/docs/mcp" } } } ``` ### Cursor [![Add to Cursor](https://cursor.com/deeplink/mcp-install-dark.svg)](cursor://anysphere.cursor-deeplink/mcp/install?name=Ayrshare-MCP\&config=eyJ1cmwiOiJodHRwczovL3d3dy5heXJzaGFyZS5jb20vZG9jcy9tY3AifQ==) 1. Use Command + Shift + P (Ctrl + Shift + P on Windows) to open the command palette. 2. Search for “Open MCP settings”. 3. Select Add custom MCP. This opens the mcp.json file. 4. In mcp.json, configure your server: ```json theme={"system"} { "mcpServers": { "Ayrshare MCP": { "url": "https://www.ayrshare.com/docs/mcp" } } } ``` ## Using MCP Server Connection Restart the app to apply the changes. In Cursor, you can test the MCP connector by typing `How do you publish a post in Ayrshare. Use the MCP server.` ![](https://mintcdn.com/ayrshare-docs/Nmrhj2Gh7WSf62Bh/images/additional/running-mcp.webp?fit=max\&auto=format\&n=Nmrhj2Gh7WSf62Bh\&q=85\&s=ed7d62f1a649cf531b6c3d1a1c2bc788) You will see the request to "Run tool" and Cursor will access the MCP server to get the response. # Ad Campaigns Source: https://www.ayrshare.com/docs/apis/ads/campaigns Create, read, and update ad campaigns that have no post behind them, such as search campaigns. A campaign is an ad that does not start from one of your posts. Use it for formats such as search ads, where you set a budget, a bidding strategy, and targeting, and the network builds the ads. To promote a post you have already published, use [Boost Post](/docs/apis/ads/facebook/boost-post) instead. > [!NOTE] > > Google accepts campaigns. See [Google Search Campaigns](/docs/apis/ads/google/search-campaigns) and [Google Performance Max Campaigns](/docs/apis/ads/google/performance-max-campaigns) for what is specific to Google. Other networks refuse a campaign request with `code: 101`, and `details` says why. On Facebook, for example, it says `campaignCreation is not supported on facebook.` ## Endpoints | Method | Path | What it does | | - | - | - | | `POST` | `/ads/{platform}/campaigns` | Create a campaign | | `GET` | `/ads/{platform}/campaigns/{id}` | Get one campaign | | `PUT` | `/ads/{platform}/campaigns/{id}` | Change one campaign | `{platform}` is the ads network. `{id}` is the campaign id returned when you created it. You can read and change only campaigns created through Ayrshare by the same user profile. ## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` **Profile-Key** `string` [Profile Key](/docs/apis/overview#profile-key-format) of a User Profile. Format: `Profile-Key: PROFILE_KEY` **Idempotency-Key** `string` *required* Required to create a campaign. Send a new unique value, such as a UUID, for each new campaign. If you repeat a request with the same key, Ayrshare returns the first response, marked `idempotentReplayed: true`, and creates nothing new. You can send the key in the body as `idempotencyKey` instead. If you send both, they must match. ## Create a Campaign `POST /ads/{platform}/campaigns` New campaigns start `paused` unless you send `status: "active"`, so nothing is spent until you choose to start. **accountId** `string` *required* The ad account to create the campaign in. **name** `string` *required* The campaign name, 1 to 255 characters. **type** `string` *required* The campaign type: `search` or `performanceMax`. Each network supports only some types. A type the network does not support answers `code: 101` and lists the supported ones. **budget** `object` *required* **properties** **amount** `number` *required* How much to spend per day, with at most two decimal places. **type** `string` *required* `daily`. **currency** `string` ISO 4217 code, such as `USD`. Defaults to the ad account's currency. Only some networks accept it. **bidding** `object` *required* **properties** **strategy** `string` *required* `maximizeConversions`, `maximizeConversionValue`, `targetCpa`, `targetRoas`, `maximizeClicks`, or `manualCpc`. Each network supports only some strategies. **targetCpa** `number` Target cost per action. Required with `targetCpa`, and not accepted with any other strategy. **targetRoas** `number` Target return on ad spend, for example `3.5` for 350%. Required with `targetRoas`, and not accepted with any other strategy. **euPoliticalAdvertising** `boolean` *required* Whether the campaign is political advertising in the EU. You must always send it. Ayrshare never fills it in for you. **schedule** `object` **properties** **start** `string` When to start: an ISO date (`2026-11-01`) or an ISO date-time with `Z` or an offset (`2026-11-01T09:00:00Z`). Defaults to now. **end** `string` When to stop, in the same format as `start`. Must be after `start`, or after now if you leave `start` out. Leave it out to run with no end date. A network that schedules whole days, such as Google, also accepts an `end` on the same day as `start`, for a one-day campaign. **targeting** `object` Each network supports only some targeting. A field the network does not support answers `code: 101`. **properties** **locations** `string[]` The network's own location ids. **languages** `string[]` The network's own language ids. **status** `string` (default: paused) `active` or `paused`. **platformOptions** `object` Options for one network only, under that network's name, for example `{ "facebook": { ... } }`. Options for any other network answer `code: 101`. ```bash cURL theme={"system"} curl \ -H "Authorization: Bearer API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: 6f1c2d4e-0b7a-4c55-9a3e-2f8d1b7c9e10" \ -X POST https://api.ayrshare.com/api/ads/google/campaigns \ -d '{ "accountId": "123-456-7890", "name": "Spring search", "type": "search", "budget": { "amount": 20, "type": "daily" }, "bidding": { "strategy": "maximizeClicks" }, "euPoliticalAdvertising": false, "platformOptions": { "google": { "finalUrl": "https://example.com/spring", "headlines": ["Spring sale", "Shoes 30% off", "Free returns"], "descriptions": ["Every shoe 30% off this week.", "Free returns on every order."], "keywords": [{ "text": "running shoes", "matchType": "phrase" }] } } }' ``` ```javascript JavaScript theme={"system"} const API_KEY = "API_KEY"; fetch("https://api.ayrshare.com/api/ads/google/campaigns", { method: "POST", headers: { "Authorization": `Bearer ${API_KEY}`, "Content-Type": "application/json", "Idempotency-Key": "6f1c2d4e-0b7a-4c55-9a3e-2f8d1b7c9e10" }, body: JSON.stringify({ "accountId": "123-456-7890", "name": "Spring search", "type": "search", "budget": { "amount": 20, "type": "daily" }, "bidding": { "strategy": "maximizeClicks" }, "euPoliticalAdvertising": false, "platformOptions": { "google": { "finalUrl": "https://example.com/spring", "headlines": ["Spring sale", "Shoes 30% off", "Free returns"], "descriptions": ["Every shoe 30% off this week.", "Free returns on every order."], "keywords": [{ "text": "running shoes", "matchType": "phrase" }] } } }) }) .then((res) => res.json()) .then((json) => console.log(json)) .catch(console.error); ``` ```python Python theme={"system"} import requests headers = { "Authorization": "Bearer API_KEY", "Idempotency-Key": "6f1c2d4e-0b7a-4c55-9a3e-2f8d1b7c9e10", } payload = { "accountId": "123-456-7890", "name": "Spring search", "type": "search", "budget": { "amount": 20, "type": "daily" }, "bidding": { "strategy": "maximizeClicks" }, "euPoliticalAdvertising": False, "platformOptions": { "google": { "finalUrl": "https://example.com/spring", "headlines": ["Spring sale", "Shoes 30% off", "Free returns"], "descriptions": ["Every shoe 30% off this week.", "Free returns on every order."], "keywords": [{ "text": "running shoes", "matchType": "phrase" }] } } } r = requests.post("https://api.ayrshare.com/api/ads/google/campaigns", headers=headers, json=payload) print(r.json()) ``` ```php PHP theme={"system"} "https://api.ayrshare.com/api/ads/google/campaigns", CURLOPT_RETURNTRANSFER => true, CURLOPT_CUSTOMREQUEST => "POST", CURLOPT_HTTPHEADER => [ "Authorization: Bearer API_KEY", "Content-Type: application/json", "Idempotency-Key: 6f1c2d4e-0b7a-4c55-9a3e-2f8d1b7c9e10" ], CURLOPT_POSTFIELDS => $payload ]); $response = curl_exec($curl); $err = curl_error($curl); curl_close($curl); if ($err) { echo "cURL Error: " . $err; } else { echo $response; } ?> ``` ```csharp C# theme={"system"} using System; using System.Net.Http; using System.Text; using System.Threading.Tasks; class Program { static async Task Main(string[] args) { var client = new HttpClient(); client.DefaultRequestHeaders.Add("Authorization", "Bearer API_KEY"); client.DefaultRequestHeaders.Add("Idempotency-Key", "6f1c2d4e-0b7a-4c55-9a3e-2f8d1b7c9e10"); var payload = """ { "accountId": "123-456-7890", "name": "Spring search", "type": "search", "budget": { "amount": 20, "type": "daily" }, "bidding": { "strategy": "maximizeClicks" }, "euPoliticalAdvertising": false, "platformOptions": { "google": { "finalUrl": "https://example.com/spring", "headlines": ["Spring sale", "Shoes 30% off", "Free returns"], "descriptions": ["Every shoe 30% off this week.", "Free returns on every order."], "keywords": [{ "text": "running shoes", "matchType": "phrase" }] } } } """; var response = await client.PostAsync( "https://api.ayrshare.com/api/ads/google/campaigns", new StringContent(payload, Encoding.UTF8, "application/json")); var content = await response.Content.ReadAsStringAsync(); Console.WriteLine(content); } } ``` ```go Go theme={"system"} package main import ( "fmt" "io" "net/http" "strings" ) func main() { payload := `{ "accountId": "123-456-7890", "name": "Spring search", "type": "search", "budget": { "amount": 20, "type": "daily" }, "bidding": { "strategy": "maximizeClicks" }, "euPoliticalAdvertising": false, "platformOptions": { "google": { "finalUrl": "https://example.com/spring", "headlines": ["Spring sale", "Shoes 30% off", "Free returns"], "descriptions": ["Every shoe 30% off this week.", "Free returns on every order."], "keywords": [{ "text": "running shoes", "matchType": "phrase" }] } } }` req, _ := http.NewRequest("POST", "https://api.ayrshare.com/api/ads/google/campaigns", strings.NewReader(payload)) req.Header.Add("Authorization", "Bearer API_KEY") req.Header.Add("Content-Type", "application/json") req.Header.Add("Idempotency-Key", "6f1c2d4e-0b7a-4c55-9a3e-2f8d1b7c9e10") resp, err := http.DefaultClient.Do(req) if err != nil { fmt.Println("Error:", err) return } defer resp.Body.Close() body, _ := io.ReadAll(resp.Body) fmt.Println(string(body)) } ``` ```java Java theme={"system"} import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; public class CreateCampaign { public static void main(String[] args) throws Exception { String payload = """ { "accountId": "123-456-7890", "name": "Spring search", "type": "search", "budget": { "amount": 20, "type": "daily" }, "bidding": { "strategy": "maximizeClicks" }, "euPoliticalAdvertising": false, "platformOptions": { "google": { "finalUrl": "https://example.com/spring", "headlines": ["Spring sale", "Shoes 30% off", "Free returns"], "descriptions": ["Every shoe 30% off this week.", "Free returns on every order."], "keywords": [{ "text": "running shoes", "matchType": "phrase" }] } } } """; HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("https://api.ayrshare.com/api/ads/google/campaigns")) .header("Authorization", "Bearer API_KEY") .header("Content-Type", "application/json") .header("Idempotency-Key", "6f1c2d4e-0b7a-4c55-9a3e-2f8d1b7c9e10") .POST(HttpRequest.BodyPublishers.ofString(payload)) .build(); HttpResponse response = HttpClient.newHttpClient() .send(request, HttpResponse.BodyHandlers.ofString()); System.out.println(response.body()); } } ``` ```ruby Ruby theme={"system"} require 'net/http' require 'uri' uri = URI.parse('https://api.ayrshare.com/api/ads/google/campaigns') request = Net::HTTP::Post.new(uri) request['Authorization'] = 'Bearer API_KEY' request['Content-Type'] = 'application/json' request['Idempotency-Key'] = '6f1c2d4e-0b7a-4c55-9a3e-2f8d1b7c9e10' request.body = <<~JSON { "accountId": "123-456-7890", "name": "Spring search", "type": "search", "budget": { "amount": 20, "type": "daily" }, "bidding": { "strategy": "maximizeClicks" }, "euPoliticalAdvertising": false, "platformOptions": { "google": { "finalUrl": "https://example.com/spring", "headlines": ["Spring sale", "Shoes 30% off", "Free returns"], "descriptions": ["Every shoe 30% off this week.", "Free returns on every order."], "keywords": [{ "text": "running shoes", "matchType": "phrase" }] } } } JSON response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http| http.request(request) end puts response.body ``` A successful create answers `status: "success"`, the `campaignId`, and the campaign's own state as `campaignStatus` (`active` or `paused`). Get and update answer the same way. ## Get a Campaign `GET /ads/{platform}/campaigns/{id}` Returns the campaign as the network reports it. A campaign that is not yours, or that does not exist, answers `code: 400`. ## Update a Campaign `PUT /ads/{platform}/campaigns/{id}` Send at least one of these fields. You cannot change `type`, `accountId`, or `euPoliticalAdvertising` after you create the campaign. **name** `string` The new campaign name. **status** `string` `active` or `paused`. A request that only sends `status: "paused"` is not blocked when Ayrshare has paused ads on that network. It still needs a linked network, and it can still fail if the network itself is down. **budget** `object` `{ "amount": number }`: the new amount to spend per day. **schedule** `object` `{ "end": string }`: the new end date, in the same format as `schedule.start`. It must be after the campaign's start. A network that schedules whole days, such as Google, also accepts the same day as the start. ## Errors These errors carry an Ayrshare `code`: | Code | When | | - | - | | `101` | The request is invalid, the network does not support campaigns or this campaign type, or the idempotency key is missing. `details` says which. Ayrshare checks for the key after it confirms the network is linked, so an unlinked profile gets the "not linked" error first. | | `400` | The campaign is not yours or does not exist. | | `424` | Ads on this network are temporarily unavailable. | A create can also answer with one of these HTTP statuses. Its body has no `code`, only `{ "action": "createAdCampaign", "status": "error", "message": "..." }`: | HTTP status | When | | - | - | | `409` | The idempotency key was used with a different request, or the request with this key is still in progress. Do not retry with a new key. Check the ad account first. | | `503` | Ayrshare could not safely reserve the idempotency key, so nothing was created. Retry later with the same key. | # Boost Post Source: https://www.ayrshare.com/docs/apis/ads/facebook/boost-post POST /ads/facebook/boost Boost an existing Facebook post into a paid ad with custom targeting, budget, scheduling, and ad goals, plus the payment method and settings Meta requires. Boost an existing Facebook post to create an ad. This endpoint allows you to convert your organic posts into paid advertisements with custom targeting, budget, and scheduling parameters. > [!NOTE] > > Meta refuses the ad until both of these are in place: > > * **A payment method on the ad account.** Add one in Meta's [Billing & payments](https://business.facebook.com/billing_hub/payment_settings/?placement=ads_manager) and attach it to the ad account you send as `accountId`. A payment method saved only on the business portfolio is not enough. > * **Personalized ads turned on (EU, EEA and Switzerland).** If the owner of the Facebook or Instagram account linked to your Facebook Page chose to see less-personalized ads, or subscribed to use Meta without ads, Meta does not let that account or the Page run ads. The owner can switch back to personalized ads, or cancel the subscription, in Accounts Center → Ad preferences → Ad experience. [Learn more](https://www.facebook.com/business/help/1563729837497242). ### Ad Goals Each ad must have a goal. The goal determines how the ad will be optimized for display. Learn more about integrating the Facebook Marketing API with Ayrshare in our [Facebook Marketing API Integration Guide](https://www.ayrshare.com/blog/facebook-ads-api-boosting-with-the-marketing-api/). ## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` **Profile-Key** `string` [Profile Key](/docs/apis/overview#profile-key-format) of a User Profile. Format: `Profile-Key: PROFILE_KEY` ## Body Parameters **idempotencyKey** `string` A unique, non-empty key for this boost request. Retry the same payload with the same key to avoid creating a duplicate ad. Reusing the key with a different payload, or for an Instagram boost, returns HTTP 409. On any other platform, the same key starts a separate boost. A request still in progress also returns 409; an unavailable reservation returns 503 without creating an ad. **accountId** `string` *required* The ID of the Facebook ad account to boost the post on. The account ID can be retrieved from the [ad accounts endpoint](/docs/apis/ads/facebook/get-ad-accounts). **adName** `string` *required* Name for your ad (appears in Facebook Ad Manager) with the format `{adName} - {postId or fbPostId} - {current date}`. If you send an `idempotencyKey`, the name also ends in ` - {request reference}`, a short reference that stays the same for that key. **bidAmount** `number` *required* Maximum bid amount in the ad account's currency, as a positive amount. **budget** `number` *required* Daily budget in the ad account's currency, as a positive amount. Meta may enforce minimums for your account and goal. **fbPostId** `string` *required* The [Facebook social post ID](/docs/apis/overview#social-post-id) of the post to boost, which allows you to create an ad from a post created directly on Facebook. This is the ID of the [post on Facebook](/docs/apis/history/history-platform), not Ayrshare. Required if `postId` is not set. **goal** `string` *required* (default: engagement) The goal of the ad. Values: `engagement`, `interactions`, `awareness_views`, `awareness_audience`, and `traffic`. See [ad goals details](/docs/apis/ads/facebook/boost-post#ad-goals) above for more information. **locations** `object` Target ad locations with an object of arrays: `countries`, `regions`, `cities`. An object that names no country, region or city, such as `{}`, is refused with code `101`; leave `locations` out to target the US. **child attributes** **countries** `array` List of country codes. ```json theme={"system"} { "countries": ["US", "CA"] } ``` **regions** `array` Regions require Facebook's region `key` value. See the [regions endpoint](/docs/apis/ads/facebook/get-ad-regions) for more information. ```json theme={"system"} { "regions": [{ "key": "3886" }] } ``` **cities** `array` Cities require: `key` (from Facebook), `radius`, and `distance_unit`. `radius` is the distance around the city: 10–50 miles or 17–80 kilometers. `distance_unit` is `mile` or `kilometer`. See the [cities endpoint](/docs/apis/ads/facebook/get-ad-cities) for more information. ```json theme={"system"} { "cities": [ { "key": "2420605", "radius": 25, "distance_unit": "mile" } ] } ``` **postId** `string` *required* The [Ayrshare post ID](/docs/apis/overview#ayrshare-post-id) of the post to boost. Required if `fbPostId` is not set. **status** `string` *required* (default: active) The status of the ad. Values: `active` and `paused`. You can later change the status of the ad using the [update ad endpoint](/docs/apis/ads/facebook/put-ad-update). **adSetBudgetSharingEnabled** `boolean` (default: false) Controls Meta's campaign-level ad set budget sharing. Meta now requires this wire field on every campaign created without a campaign-level budget, so Ayrshare always sends it — defaulting to `false` for backward compatibility. Set to `true` to let Meta share up to \~20% of an ad set's budget across other ad sets in the same campaign to optimize overall performance. See Meta's [ad campaign group reference](https://developers.facebook.com/docs/marketing-api/reference/ad-campaign-group/) for details. **callToAction** `object` Add a call-to-action button, such as **Apply Now** or **Shop Now**, to the boosted post. The button opens the URL you supply in `link`, or, with `destination`, a Messenger, WhatsApp or Instagram Direct chat instead (see [Open a Conversation Instead of a Link](/docs/apis/ads/facebook/boost-post#message-destination)). ```json theme={"system"} { "callToAction": { "type": "APPLY_NOW", "link": "https://www.mysite.com/careers/social-media-manager", "linkTitle": "Social Media Manager" } } ``` **child attributes** **type** `string` *required* The button label. Values: `LEARN_MORE`, `APPLY_NOW`, `SIGN_UP`, `SHOP_NOW`, `BOOK_TRAVEL`, `GET_QUOTE`, `CONTACT_US`, `DOWNLOAD`, and `SUBSCRIBE`. Case-insensitive on input, so `apply_now`, `Apply_Now`, and `APPLY_NOW` are all accepted. With `destination`, use the messaging button instead: `MESSAGE_PAGE`, `WHATSAPP_MESSAGE`, or `INSTAGRAM_MESSAGE`. **link** `string` The destination the button opens. Must be an absolute `http` or `https` URL. Relative paths, `mailto:` links, and malformed URLs are rejected. Required unless `destination` is set. **linkTitle** `string` Optional headline displayed with the button. **destination** `string` Open a chat instead of a link: `messenger`, `whatsapp`, or `instagramDm`. Nested form only. See [Open a Conversation Instead of a Link](/docs/apis/ads/facebook/boost-post#message-destination). > [!WARNING] > > A `callToAction` that opens a link is only supported with the `traffic` [ad goal](/docs/apis/ads/facebook/boost-post#ad-goals). A button with `destination` uses `engagement` or `interactions` instead, as described in [Open a Conversation Instead of a Link](/docs/apis/ads/facebook/boost-post#message-destination). > Sending a link button with `engagement`, `interactions`, `awareness_views`, or `awareness_audience` returns a `400` with `code: 101` naming the compatible goals. > Previously the value was accepted on any goal and then silently ignored, producing an ad with no button. `callToAction.link` supplies the destination URL. When Meta renders the call to action as a button on the existing post, `urlTags` append to that button link. `goal: "traffic"` without a `callToAction` is allowed and creates a traffic ad with no button. The `callToAction` you send is echoed back in the boost response with `type` upper-cased — see the *Post Boosted with a Call to Action* example — and is also returned on the ad afterwards by [Get Boosted Ads](/docs/apis/ads/facebook/get-boosted-ads). To open a Messenger, WhatsApp or Instagram Direct chat instead of a link, see [Open a Conversation Instead of a Link](/docs/apis/ads/facebook/boost-post#message-destination). **tracking** `object` Track the ad using a Facebook Pixel. **child attributes** **pixelId** `number` *required* The ID of the Facebook Pixel to track the ad. ```json theme={"system"} { "pixelId": 1234567890 } ``` **urlTags** `array` Add UTM tags to the ad URL. ```json theme={"system"} { "urlTags": ["utm_source=ayrshare", "utm_medium=social", "utm_campaign=ayrshare-social"] } ``` For example if the linking URL is `https://www.mysite.com/my-post` and the URL tags added are: `utm_source=ayrshare`, `utm_medium=social`, and `utm_campaign=ayrshare-social`. The ad URL will be: `https://www.mysite.com/my-post?utm_source=ayrshare&utm_medium=social&utm_campaign=ayrshare-social`. **specialAdCategories** `array` Meta requires advertisers in [special ad categories](https://www.facebook.com/business/help/298000447747885?helpref=faq_content) to must self-identify their campaign category. If your business is in one of these categories, you must select the appropriate category when boosting your post. The following values are supported: Values are case-insensitive. An unrecognized value is rejected with a `400` and `code: 101` listing the accepted values. Previously an unrecognized value was silently dropped and the campaign was created as uncategorized, so a typo left a regulated ad running with no category declared. The accepted-values list in that error message is written in Meta's upper-case form and also names the alternative spellings above, so it reads `NONE, HOUSING, FINANCIAL_PRODUCTS_SERVICES, EMPLOYMENT, ISSUES_ELECTIONS_POLITICS, FINANCIAL_PRODUCT_SERVICES, CREDIT`. Those are the same five categories listed here — `FINANCIAL_PRODUCT_SERVICES` and `CREDIT` are the equivalents of `financial_product_services`, not extra categories. The identical message is returned by the [interests endpoint](/docs/apis/ads/facebook/get-ad-interests#param-special-ad-categories). Repeating a category, or sending two spellings of the same one such as `["credit", "financial_product_services"]`, is accepted and collapses to a single category before it reaches Meta. An empty value is rejected with a `400` and `code: 101`. To find interest IDs that Meta allows for one of these categories, pass the same value to the [interests endpoint](/docs/apis/ads/facebook/get-ad-interests#param-special-ad-categories). Meta also needs the countries a special category ad runs in. Ayrshare takes them from `locations`: the countries you list, plus the country of each region and city. So a `housing` boost that targets a city in the UK is set up for the UK, with nothing extra in your request. Without `locations` the ad runs in the US, and the campaign is set up for the US. Meta also sets a minimum radius around each city for these categories. A UK city with a 17 kilometer radius was rejected with `#2909039`, which asks for at least 25 kilometers (15 miles), so use 25 kilometers or more. > [!WARNING] > > Meta requires advertisers who have self-identified their campaign category. > Meta uses human reviewers and machine-learning to identify these kinds of ads. > If you send us an incorrect `specialAdCategory`, there is a risk your ads will be paused until the campaign is adjusted. **endDate** `string` End date and time in ISO 8601 format (must be at least 30 hours after start), for example `2025-03-01T00:00:00Z`. If not set, the ad will run indefinitely and have end date of `ongoing`. **excludedLocations** `object` Exclude locations using an object with arrays of `countries`, `regions`, and `cities`. **child attributes** **countries** `array` List of country codes to exclude. ```json theme={"system"} { "countries": ["US", "CA"] } ``` **regions** `array` Regions require Facebook's region `key` value. See the [regions endpoint](/docs/apis/ads/facebook/get-ad-regions) for more information. ```json theme={"system"} { "regions": [{ "key": "3886" }] } ``` **cities** `array` Cities require: `key` (from Facebook), `radius`, and `distance_unit`. `radius` is the distance around the city: 10–50 miles or 17–80 kilometers. `distance_unit` is `mile` or `kilometer`. See the [cities endpoint](/docs/apis/ads/facebook/get-ad-cities) for more information. ```json theme={"system"} { "cities": [ { "key": "2420605", "radius": 25, "distance_unit": "mile" } ] } ``` **gender** `string` (default: all) The gender of the audience. Values: `all`, `male`, `female`. **interests** `array` The target interests of the ad as an array of Facebook [interest ids](/docs/apis/ads/facebook/get-ad-interests). **maxAge** `number` (default: 65) Maximum age for targeting the ad (default: 65). **minAge** `number` (default: 18) Minimum age for targeting the ad (default: 18). **startDate** `string` Start date and time in ISO 8601 format, for example `2025-03-01T00:00:00Z`. If not set, the ad will start immediately. **dsaBeneficiary** `string` The beneficiary of the ad for Digital Services Act (DSA) compliance for EU countries. Must be set together with `dsaPayor` if either is provided. Please refer to our [DSA guide](/docs/apis/ads/facebook/get-dsa-recommendations) for more information. **dsaPayor** `string` The payor of the ad for Digital Services Act (DSA) compliance for EU countries. Must be set together with `dsaBeneficiary` if either is provided. ## Lifetime Budgets The body above sets a daily budget on the ad set. For a lifetime budget, send the nested form of the boost body instead. In the nested form, `accountId`, `postId` (or `socialPostId` in place of `fbPostId`), `goal`, `status` and `callToAction` stay at the top level. Three objects replace the flat budget, date and targeting fields: `budget`, `schedule` (`start`, `end`) and `targeting` (`locations`, `excludedLocations`, `ageMin`, `ageMax`, `genders`, `interests`). The remaining fields, `adName`, `bidAmount`, `specialAdCategories`, `adSetBudgetSharingEnabled`, `link`, `tracking`, `urlTags`, `dsaBeneficiary` and `dsaPayor`, move under `platformOptions.facebook`. ```json theme={"system"} { "accountId": "123456789", "postId": "Ut1fWU6XkqkMayHGnJZ", "goal": "traffic", "budget": { "amount": 50, "type": "lifetime" }, "schedule": { "start": "2026-11-01T00:00:00Z", "end": "2026-11-08T00:00:00Z" }, "targeting": { "ageMin": 25, "ageMax": 45, "locations": { "countries": ["US"] } }, "platformOptions": { "facebook": { "adName": "November boost", "bidAmount": 2 } } } ``` * `budget.type`: `daily` (the default) or `lifetime`. A lifetime budget is spent between the start and end dates, so it needs `schedule.end`. Without one the request is rejected with a `400` and `code: 101`. Meta's ad set budget sharing works with daily budgets only, so a lifetime budget with `adSetBudgetSharingEnabled: true` is also rejected with `code: 101`. The response adds `budgetType: "lifetime"` for a lifetime budget. A daily budget does not return `budgetType`, so treat a missing field as `daily`. ## Campaign Budgets To hold the budget on the campaign instead of the ad set, send `budget.level: "campaign"` in the nested form shown above. The ad set then has no budget of its own. It works with daily and lifetime budgets. ```json theme={"system"} { "accountId": "123456789", "postId": "Ut1fWU6XkqkMayHGnJZ", "goal": "traffic", "budget": { "amount": 50, "type": "lifetime", "level": "campaign" }, "schedule": { "start": "2026-11-01T00:00:00Z", "end": "2026-11-08T00:00:00Z" }, "platformOptions": { "facebook": { "adName": "November boost", "bidAmount": 2 } } } ``` * `budget.level`: `adSet` (the default) or `campaign`. A campaign budget cannot be combined with `adSetBudgetSharingEnabled`. Sending both is rejected with a `400` and `code: 101`. * The response adds `budgetLevel: "campaign"` for a campaign budget. An ad set budget does not return `budgetLevel`, so treat a missing field as `adSet`. * In [History](/docs/apis/ads/facebook/get-ad-history), `budgetDaily` shows the campaign's daily budget for an ad with a daily campaign budget. If several ads share one campaign budget, each shows the whole campaign budget, not its share. History does not show lifetime budgets, on the campaign or the ad set. Once a lifetime campaign budget is spent, every ad in that campaign shows `deliveryStatus: "COMPLETED"` after the next nightly update, including an ad that is scheduled or in review. A rejected ad stays `REJECTED`. ## Boost into an Existing Campaign or Ad Set By default every boost creates its own campaign, ad set and ad. To add the boost to a campaign or ad set you already run in Meta, send the nested form with `attachTo`. * `attachTo.campaignId`: the boost creates a new ad set and ad inside that campaign. Send `budget`, `targeting` and `bidAmount` as usual: they apply to the new ad set. * `attachTo.adSetId`: the boost creates only the ad, inside that ad set. Also send `attachTo.campaignId`, the campaign the ad set belongs to. Leave out `budget`, `targeting` and `platformOptions.facebook.bidAmount`: the ad set keeps its own. Sending any of them returns a `400` with `code: 101`. ```json theme={"system"} { "accountId": "123456789", "postId": "Ut1fWU6XkqkMayHGnJZ", "goal": "engagement", "attachTo": { "campaignId": "120210000000000001", "adSetId": "120210000000000002" }, "platformOptions": { "facebook": { "adName": "November boost" } } } ``` * The campaign or ad set must be in the ad account named by `accountId`, and must not belong to another user's boosts. Otherwise the request returns a `400`. A Facebook boost also cannot attach to a campaign an Instagram boost created, and the reverse. * `attachTo` cannot be combined with `budget.level: "campaign"`, `specialAdCategories` or `adSetBudgetSharingEnabled`, which are set on a new campaign. Sending them returns `code: 101`. * Pausing or activating the boost changes only what the boost created: the ad, and the ad set when the boost created it. The campaign or ad set you named in `attachTo` keeps its own status. The ad runs only while the campaign and ad set above it are active. This also applies when Ayrshare pauses your ads, for example when a profile is deleted. * If the boost fails, the campaign or ad set named in `attachTo` is never deleted. * If the ad set you attach to sits in a campaign that holds the budget (a campaign budget), [History](/docs/apis/ads/facebook/get-ad-history) reports the boost like any ad with a campaign budget: `budgetDaily` shows the whole campaign budget, not the boost's share, and once a lifetime campaign budget is spent, every ad Ayrshare tracks in that campaign shows `deliveryStatus: "COMPLETED"` after the next nightly update, including an ad that is scheduled or in review. A rejected ad stays `REJECTED`. ## Open a Conversation Instead of a Link A boost's button can open a chat with your business instead of a web page. Send the nested form with `callToAction.destination`, and leave out `callToAction.link`. | `destination` | Opens | `callToAction.type` | | - | - | - | | `messenger` | Messenger | `MESSAGE_PAGE` | | `whatsapp` | WhatsApp | `WHATSAPP_MESSAGE` | | `instagramDm` | Instagram Direct | `INSTAGRAM_MESSAGE` | ```json theme={"system"} { "accountId": "123456789", "postId": "Ut1fWU6XkqkMayHGnJZ", "goal": "engagement", "budget": { "amount": 20 }, "callToAction": { "type": "WHATSAPP_MESSAGE", "destination": "whatsapp" }, "platformOptions": { "facebook": { "adName": "Chat with us", "bidAmount": 2 } } } ``` * Use the `engagement` or `interactions` [ad goal](/docs/apis/ads/facebook/boost-post#ad-goals). The ad is then optimized for conversations: Meta shows it to people likely to start a chat. Other goals return a `400` with `code: 101`. * `callToAction.type` must match the destination, as in the table. Any casing is accepted. * `callToAction.link` and `callToAction.linkTitle` cannot be sent with `destination`, because the button opens a chat, not a page. Without `destination`, `link` is still required. * People message the account linked to your profile. For `whatsapp`, its Facebook Page must have a WhatsApp Business number connected in Meta. * `destination` cannot be combined with `attachTo.adSetId`: the existing ad set keeps the destination it already has. With `attachTo.campaignId`, the campaign must have Meta's `OUTCOME_ENGAGEMENT` objective; otherwise the request returns a `400` with `code: 101`. * The response, and [Get Boosted Ads](/docs/apis/ads/facebook/get-boosted-ads), return `callToAction` with its `destination`. ```bash cURL theme={"system"} curl -X POST https://api.ayrshare.com/api/ads/facebook/boost \ -H "Authorization: Bearer API_KEY" \ -H "Content-Type: application/json" \ -d '{ "postId": "1234567890", "accountId": "1234567890", "adName": "My Ad", "status": "active", "goal": "engagement", "minAge": 18, "maxAge": 65, "locations": { "countries": ["US"] }, "budget": 100, "bidAmount": 1, "startDate": "2025-03-01T00:00:00Z", "endDate": "2025-03-07T23:59:59Z", "interests": [1234567890, 1234567891] }' ``` ```bash cURL: traffic goal with a call to action theme={"system"} curl -X POST https://api.ayrshare.com/api/ads/facebook/boost \ -H "Authorization: Bearer API_KEY" \ -H "Content-Type: application/json" \ -d '{ "postId": "1234567890", "accountId": "1234567890", "adName": "Nurse Practitioner Hiring", "status": "active", "goal": "traffic", "callToAction": { "type": "APPLY_NOW", "link": "https://example.com/careers/nurse-practitioner", "linkTitle": "Apply today" }, "specialAdCategories": ["employment"], "minAge": 18, "maxAge": 65, "locations": { "countries": ["US"] }, "budget": 100, "bidAmount": 1, "startDate": "2025-03-01T00:00:00Z", "endDate": "2025-03-07T23:59:59Z" }' ``` ```javascript JavaScript theme={"system"} const API_KEY = "API_KEY"; fetch("https://api.ayrshare.com/api/ads/facebook/boost", { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${API_KEY}` }, body: JSON.stringify({ postId: "1234567890", accountId: "1234567890", adName: "My Ad", status: "active", goal: "engagement", minAge: 18, maxAge: 65, locations: { countries: ["US"] }, budget: 100, bidAmount: 1, startDate: "2025-03-01T00:00:00Z", endDate: "2025-03-07T23:59:59Z", interests: [1234567890, 1234567891] }) }) .then((res) => res.json()) .then((json) => console.log(json)) .catch(console.error); ``` ```python Python theme={"system"} import requests API_KEY = "API_KEY" url = "https://api.ayrshare.com/api/ads/facebook/boost" payload = { "postId": "1234567890", "accountId": "1234567890", "adName": "My Ad", "status": "active", "goal": "engagement", "minAge": 18, "maxAge": 65, "locations": {"countries": ["US"]}, "budget": 100, "bidAmount": 1, "startDate": "2025-03-01T00:00:00Z", "endDate": "2025-03-07T23:59:59Z", "interests": [1234567890, 1234567891] } response = requests.post(url, headers={"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"}, json=payload) print(response.json()) ``` ```php PHP theme={"system"} "1234567890", "accountId" => "1234567890", "adName" => "My Ad", "status" => "active", "goal" => "engagement", "minAge" => 18, "maxAge" => 65, "locations" => ["countries" => ["US"]], "budget" => 100, "bidAmount" => 1, "startDate" => "2025-03-01T00:00:00Z", "endDate" => "2025-03-07T23:59:59Z", "interests" => [1234567890, 1234567891] ]; $ch = curl_init("https://api.ayrshare.com/api/ads/facebook/boost"); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload)); curl_setopt($ch, CURLOPT_HTTPHEADER, [ "Content-Type: application/json", "Authorization: Bearer " . $API_KEY ]); $response = curl_exec($ch); $result = json_decode($response, true); print_r($result); curl_close($ch); ``` ```csharp C# theme={"system"} using System; using System.Net.Http; using System.Net.Http.Headers; using System.Text; using System.Text.Json; using System.Threading.Tasks; class Program { static async Task Main() { string API_KEY = "API_KEY"; var payload = new { postId = "1234567890", accountId = "1234567890", adName = "My Ad", status = "active", goal = "engagement", minAge = 18, maxAge = 65, locations = new { countries = new[] { "US" } }, budget = 100, bidAmount = 1, startDate = "2025-03-01T00:00:00Z", endDate = "2025-03-07T23:59:59Z", interests = new[] { 1234567890, 1234567891 } }; using var client = new HttpClient(); client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", API_KEY); var content = new StringContent( JsonSerializer.Serialize(payload), Encoding.UTF8, "application/json" ); var response = await client.PostAsync("https://api.ayrshare.com/api/ads/facebook/boost", content); var responseBody = await response.Content.ReadAsStringAsync(); Console.WriteLine(responseBody); } } ``` ```go Go theme={"system"} package main import ( "bytes" "encoding/json" "fmt" "io/ioutil" "net/http" ) func main() { apiKey := "API_KEY" payload := map[string]interface{}{ "postId": "1234567890", "accountId": "1234567890", "adName": "My Ad", "status": "active", "goal": "engagement", "minAge": 18, "maxAge": 65, "locations": map[string]interface{}{"countries": []string{"US"}}, "budget": 100, "bidAmount": 1, "startDate": "2025-03-01T00:00:00Z", "endDate": "2025-03-07T23:59:59Z", "interests": []int{1234567890, 1234567891}, } jsonData, err := json.Marshal(payload) if err != nil { fmt.Println("Error marshaling JSON:", err) return } req, err := http.NewRequest("POST", "https://api.ayrshare.com/api/ads/facebook/boost", bytes.NewBuffer(jsonData)) if err != nil { fmt.Println("Error creating request:", err) return } req.Header.Set("Content-Type", "application/json") req.Header.Set("Authorization", "Bearer "+apiKey) client := &http.Client{} resp, err := client.Do(req) if err != nil { fmt.Println("Error sending request:", err) return } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { fmt.Println("Error reading response:", err) return } fmt.Println(string(body)) } ``` ```java Java theme={"system"} import java.io.IOException; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import com.fasterxml.jackson.databind.ObjectMapper; public class BoostPost { public static void main(String[] args) { String API_KEY = "API_KEY"; String url = "https://api.ayrshare.com/api/ads/facebook/boost"; Map payload = new HashMap<>(); payload.put("postId", "1234567890"); payload.put("accountId", "1234567890"); payload.put("adName", "My Ad"); payload.put("status", "active"); payload.put("goal", "engagement"); payload.put("minAge", 18); payload.put("maxAge", 65); Map locations = new HashMap<>(); locations.put("countries", Arrays.asList("US")); payload.put("locations", locations); payload.put("budget", 100); payload.put("bidAmount", 1); payload.put("startDate", "2025-03-01T00:00:00Z"); payload.put("endDate", "2025-03-07T23:59:59Z"); payload.put("interests", Arrays.asList(1234567890, 1234567891)); try { ObjectMapper objectMapper = new ObjectMapper(); String requestBody = objectMapper.writeValueAsString(payload); HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(url)) .header("Content-Type", "application/json") .header("Authorization", "Bearer " + API_KEY) .POST(HttpRequest.BodyPublishers.ofString(requestBody)) .build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); System.out.println(response.body()); } catch (IOException | InterruptedException e) { e.printStackTrace(); } } } ``` ```ruby Ruby theme={"system"} require 'net/http' require 'uri' require 'json' API_KEY = "API_KEY" uri = URI.parse("https://api.ayrshare.com/api/ads/facebook/boost") payload = { postId: "1234567890", accountId: "1234567890", adName: "My Ad", status: "active", goal: "engagement", minAge: 18, maxAge: 65, locations: { countries: ["US"] }, budget: 100, bidAmount: 1, startDate: "2025-03-01T00:00:00Z", endDate: "2025-03-07T23:59:59Z", interests: [1234567890, 1234567891] } http = Net::HTTP.new(uri.host, uri.port) http.use_ssl = true request = Net::HTTP::Post.new(uri.request_uri) request["Content-Type"] = "application/json" request["Authorization"] = "Bearer #{API_KEY}" request.body = payload.to_json response = http.request(request) puts response.body ``` ```json 200: Post Boosted theme={"system"} { "status": "success", "adId": "120217670757750410", "adName": "API Post - DE6gpw8kxlonHy2b7Lo - 2025-03-26T23:42:43", "adStatus": "active", "bidAmount": 10, "budget": 100, "endDate": "2026-03-28T22:30:00Z", "goal": { "title": "Get More Engagement", "description": "This goal seeks to increase engagement...", "type": "engagement" }, "interests": [ "6003195554098" ], "locations": { "countries": ["US"] }, "maxAge": 65, "minAge": 18, "postId": "DE6gpw8kxlonHy2b7Lo", "startDate": "2026-03-26T22:30:00Z" } ``` ```json 200: Post Boosted with a Call to Action theme={"system"} { "status": "success", "adId": "120217670757750411", "adName": "Nurse Practitioner Hiring - DE6gpw8kxlonHy2b7Lo - 2025-03-26T23:42:43", "adStatus": "active", "bidAmount": 1, "budget": 100, "callToAction": { "type": "APPLY_NOW", "link": "https://example.com/careers/nurse-practitioner", "linkTitle": "Apply today" }, "endDate": "2025-03-07T23:59:59Z", "goal": { "title": "Drive Traffic", "description": "Sends people from the boosted post to a destination off Facebook...", "type": "traffic" }, "locations": { "countries": ["US"] }, "maxAge": 65, "minAge": 18, "postId": "DE6gpw8kxlonHy2b7Lo", "startDate": "2025-03-01T00:00:00Z" } ``` ```json 400: Invalid Account ID theme={"system"} { "action": "boost post", "status": "error", "code": 369, "message": "Unable to boost post. Please try again or contact us if the issue persists.", "details": "The accountId likely does not exist. Please check the accountId and try again." } ``` ```json 400: Missing Required Parameters theme={"system"} { "action": "request", "status": "error", "code": 101, "message": "Missing or incorrect parameters. Please verify with the docs. https://www.ayrshare.com/docs/apis", "details": "Missing required fields: budget" } ``` # Accounts Source: https://www.ayrshare.com/docs/apis/ads/facebook/get-ad-accounts GET /ads/facebook/accounts List the Facebook ad accounts linked to the authenticated profile, with account status, aggregate performance metrics, and results cached for 10 minutes. Retrieve available Facebook ad accounts associated with the authenticated profile. This endpoint allows you to access basic account information as well as key metrics for the ad accounts.
  • The `metrics` object contains aggregate performance data across all ads in the account
  • Results are cached for 10 minutes to optimize performance and reduce API calls to Facebook
  • Account status in the JSON response may be one of: `active`, `disabled`, `unsettled`, `pending review`, or `closed`
## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` **Profile-Key** `string` [Profile Key](/docs/apis/overview#profile-key-format) of a User Profile. Format: `Profile-Key: PROFILE_KEY` ## Query Parameters **limit** `number` (default: 100) Limit the number of ad accounts returned. ```bash cURL theme={"system"} curl \ -H "Authorization: Bearer API_KEY" \ -X GET https://api.ayrshare.com/api/ads/facebook/accounts ``` ```javascript JavaScript theme={"system"} const API_KEY = "API_KEY"; fetch("https://api.ayrshare.com/api/ads/facebook/accounts", { method: "GET", headers: { "Authorization": `Bearer ${API_KEY}` } }) .then((res) => res.json()) .then((json) => console.log(json)) .catch(console.error); ``` ```python Python theme={"system"} import requests headers = {'Authorization': 'Bearer API_KEY'} r = requests.get('https://api.ayrshare.com/api/ads/facebook/accounts', headers=headers) print(r.json()) ``` ```php PHP theme={"system"} "https://api.ayrshare.com/api/ads/facebook/accounts", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer API_KEY" ] ]); $response = curl_exec($curl); curl_close($curl); echo $response; ?> ``` ```csharp C# theme={"system"} using System; using System.Net.Http; using System.Threading.Tasks; class Program { static async Task Main(string[] args) { string apiKey = "API_KEY"; using (HttpClient client = new HttpClient()) { client.DefaultRequestHeaders.Add("Authorization", $"Bearer {apiKey}"); HttpResponseMessage response = await client.GetAsync("https://api.ayrshare.com/api/ads/facebook/accounts"); string content = await response.Content.ReadAsStringAsync(); Console.WriteLine(content); } } } ``` ```go Go theme={"system"} package main import ( "fmt" "io/ioutil" "net/http" ) func main() { apiKey := "API_KEY" req, _ := http.NewRequest("GET", "https://api.ayrshare.com/api/ads/facebook/accounts", nil) req.Header.Add("Authorization", "Bearer " + apiKey) client := &http.Client{} resp, err := client.Do(req) if err != nil { fmt.Println(err) return } defer resp.Body.Close() body, _ := ioutil.ReadAll(resp.Body) fmt.Println(string(body)) } ``` ```java Java theme={"system"} import java.io.BufferedReader; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URL; public class GetAdAccounts { public static void main(String[] args) { try { String apiKey = "API_KEY"; URL url = new URL("https://api.ayrshare.com/api/ads/facebook/accounts"); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("GET"); conn.setRequestProperty("Authorization", "Bearer " + apiKey); BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream())); String inputLine; StringBuffer response = new StringBuffer(); while ((inputLine = in.readLine()) != null) { response.append(inputLine); } in.close(); System.out.println(response.toString()); } catch (Exception e) { e.printStackTrace(); } } } ``` ```ruby Ruby theme={"system"} require 'net/http' require 'json' uri = URI('https://api.ayrshare.com/api/ads/facebook/accounts') req = Net::HTTP::Get.new(uri) req['Authorization'] = 'Bearer API_KEY' response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) } puts response.body ``` ```json 200: Success theme={"system"} { "status": "success", "adAccounts": [ { "accountId": "274948345", "ageInDays": 2870.86, "amountSpent": 191.33, "balance": 7.84, "budgetRemaining": 1, "business": { "name": "Fun Fun Fun", "city": "New York", "country": "US", "state": "NY", "street": "178 Columbus Ave", "zip": "10023" }, "created": "2012-01-05T03:11:31-0800", "currency": "USD", "disableReason": null, "fundingSource": { "id": "13157487252022", "type": "Credit Card" }, "hasNotificationsEnabled": true, "isPersonal": false, "isPrepayAccount": false, "metrics": { "spend": 191.33, "impressions": 24994, "reach": 20410, "clicks": 622, "ctr": 2.488597, "cpm": 7.655037, "cpp": 9.374326, "frequency": 1.224596, "uniqueClicks": 432, "uniqueCtr": 2.11661, "costPerUniqueClick": 0.442894, "inlineLinkClicks": 538, "costPerInlineLinkClick": 0.355632, "outboundClicks": 468, "costPerOutboundClick": 0.408825, "websiteCtr": [ { "action_type": "link_click", "value": "2.152517" } ], "accountCurrency": "USD", "accountName": "John Smith", "accountId": "274948321" }, "minDailyBudget": 1, "name": "John Smith", "owner": "3205611418413", "spendCap": 0, "status": "Active", "tax": { "id": "824087134", "type": "Business Tax ID (EIN/SSN)", "status": "Pending", "isRequired": false }, "timezoneName": "America/Los_Angeles", "tosAccepted": { "webCustomAudienceTos": true, "customAudienceTos": true } } ], "count": 1, "lastUpdated": "2025-03-26T19:33:51.571Z", "nextUpdate": "2025-03-26T19:44:51.571Z" } ``` ```json 400: Ad accounts error theme={"system"} { "action": "get ad accounts", "status": "error", "code": 366, "message": "Error getting ad accounts. Please verify you have an active ad account at Facebook." } ``` # Campaigns Source: https://www.ayrshare.com/docs/apis/ads/facebook/get-ad-campaigns GET /ads/facebook/campaigns Fetch campaigns for one Facebook ad account by account ID, including remaining budget, campaign status, performance metrics, and scheduling details. Retrieve ad campaigns for a specific Facebook ad account. This endpoint provides detailed information about campaigns, including budget, status, performance metrics, and scheduling details.
  • Campaign status may be one of: "ACTIVE", "PAUSED", "DELETED", "ARCHIVED", "IN\_PROCESS", or "WITH\_ISSUES"
  • The `metrics` object contains performance data for the campaign
  • The `budgetRemaining` field represents the remaining budget in the account's currency
  • Results are cached for 10 minutes to optimize performance
## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` **Profile-Key** `string` [Profile Key](/docs/apis/overview#profile-key-format) of a User Profile. Format: `Profile-Key: PROFILE_KEY` ## Query Parameters **accountId** `string` *required* The ID of the ad account to retrieve campaigns for. The ad account id, with or without the `act_` prefix (`1234567890` or `act_1234567890`). **limit** `number` (default: 100) Limit the number of campaigns returned. ```bash cURL theme={"system"} curl \ -H "Authorization: Bearer API_KEY" \ -X GET https://api.ayrshare.com/api/ads/facebook/campaigns?accountId=1234567890 ``` ```javascript JavaScript theme={"system"} const API_KEY = "API_KEY"; fetch("https://api.ayrshare.com/api/ads/facebook/campaigns?accountId=1234567890", { method: "GET", headers: { "Authorization": `Bearer ${API_KEY}` } }) .then((res) => res.json()) .then((json) => console.log(json)) .catch(console.error); ``` ```python Python theme={"system"} import requests headers = {'Authorization': 'Bearer API_KEY'} r = requests.get('https://api.ayrshare.com/api/ads/facebook/campaigns?accountId=1234567890', headers=headers) print(r.json()) ``` ```php PHP theme={"system"} [ 'header' => "Authorization: Bearer " . $apiKey, 'method' => 'GET' ] ]; $context = stream_context_create($options); $response = file_get_contents($url, false, $context); $result = json_decode($response, true); print_r($result); ?> ``` ```csharp C# theme={"system"} using System; using System.Net.Http; using System.Threading.Tasks; class Program { static async Task Main() { string apiKey = "API_KEY"; string url = "https://api.ayrshare.com/api/ads/facebook/campaigns?accountId=1234567890"; using (HttpClient client = new HttpClient()) { client.DefaultRequestHeaders.Add("Authorization", $"Bearer {apiKey}"); HttpResponseMessage response = await client.GetAsync(url); string content = await response.Content.ReadAsStringAsync(); Console.WriteLine(content); } } } ``` ```go Go theme={"system"} package main import ( "fmt" "io/ioutil" "net/http" ) func main() { apiKey := "API_KEY" url := "https://api.ayrshare.com/api/ads/facebook/campaigns?accountId=1234567890" req, _ := http.NewRequest("GET", url, nil) req.Header.Add("Authorization", "Bearer " + apiKey) client := &http.Client{} resp, err := client.Do(req) if err != nil { fmt.Println(err) return } defer resp.Body.Close() body, _ := ioutil.ReadAll(resp.Body) fmt.Println(string(body)) } ``` ```java Java theme={"system"} import java.io.BufferedReader; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URL; public class Main { public static void main(String[] args) { try { String apiKey = "API_KEY"; URL url = new URL("https://api.ayrshare.com/api/ads/facebook/campaigns?accountId=1234567890"); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("GET"); conn.setRequestProperty("Authorization", "Bearer " + apiKey); BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream())); String inputLine; StringBuffer response = new StringBuffer(); while ((inputLine = in.readLine()) != null) { response.append(inputLine); } in.close(); System.out.println(response.toString()); } catch (Exception e) { e.printStackTrace(); } } } ``` ```ruby Ruby theme={"system"} require 'net/http' require 'uri' require 'json' api_key = "API_KEY" uri = URI.parse("https://api.ayrshare.com/api/ads/facebook/campaigns?accountId=1234567890") request = Net::HTTP::Get.new(uri) request["Authorization"] = "Bearer #{api_key}" response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == 'https') do |http| http.request(request) end puts JSON.parse(response.body) ``` ```json 200: Success theme={"system"} { "status": "success", "campaigns": [ { "accountId": "8501481455633", "boostedObjectId": "67263320208733", "budgetRemaining": 0, "buyingType": "AUCTION", "campaignId": "1202176707545833", "configuredStatus": "PAUSED", "created": "2025-03-26T19:42:43-0400", "dailyBudget": 0, "effectiveStatus": "ACTIVE", "endDate": "2026-03-28T18:30:00-0400", "lifetimeBudget": 0, "metrics": {}, "name": "API Post - DE6gpw8kxlonHy6eb33 - 2025-03-26T23:42:43", "spendCap": 0, "startDate": "2026-03-26T18:30:00-0400", "status": "ACTIVE" } ], "count": 1, "lastUpdated": "2025-03-27T00:57:15.339Z", "nextUpdate": "2025-03-27T01:08:15.339Z" } ``` ```json 400: Ad campaigns error theme={"system"} { "action": "get ad campaigns", "status": "error", "code": 367, "message": "Error getting ad campaigns. Please verify you have an active ad campaign account." } ``` # Cities Source: https://www.ayrshare.com/docs/apis/ads/facebook/get-ad-cities GET /ads/facebook/cities Search Facebook cities by name for targeting when boosting an ad. Each result includes its parent region, radius targeting support, and targeting flags. Search for cities to be used for targeting when boosting an ad.
  • Cities use Facebook's `key` identifier and support radius targeting.
  • Each city result includes its parent region as `region` (the region name) and `regionId` (the region's numeric identifier), so you can target the city's region when it is available.
  • If a returned city has `supportsRegion` `true`, the region for this city is available for targeting. If `supportsCity` is `true`, this city is available for targeting.
  • Some locations may be returned as `subcity` with additional hierarchy fields.
## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` **Profile-Key** `string` [Profile Key](/docs/apis/overview#profile-key-format) of a User Profile. Format: `Profile-Key: PROFILE_KEY` ## Query Parameters **search** `string` The search string to filter cities. Provide a partial or full city name. **limit** `number` (default: 25) Maximum number of cities to return. ```bash cURL theme={"system"} curl \ -H "Authorization: Bearer API_KEY" \ -X GET "https://api.ayrshare.com/api/ads/facebook/cities?search=Manhattan" ``` ```javascript JavaScript theme={"system"} const API_KEY = "API_KEY"; fetch("https://api.ayrshare.com/api/ads/facebook/cities?search=Manhattan", { method: "GET", headers: { "Authorization": `Bearer ${API_KEY}` } }) .then((res) => res.json()) .then((json) => console.log(json)) .catch(console.error); ``` ```python Python theme={"system"} import requests headers = {'Authorization': 'Bearer API_KEY'} r = requests.get('https://api.ayrshare.com/api/ads/facebook/cities?search=Manhattan', headers=headers) print(r.json()) ``` ```php PHP theme={"system"} "https://api.ayrshare.com/api/ads/facebook/cities?search=Manhattan", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer API_KEY" ], ]); $response = curl_exec($curl); curl_close($curl); echo $response; ?> ``` ```csharp C# theme={"system"} using System; using System.Net.Http; using System.Threading.Tasks; class Program { static async Task Main() { var client = new HttpClient(); client.DefaultRequestHeaders.Add("Authorization", "Bearer API_KEY"); var response = await client.GetAsync("https://api.ayrshare.com/api/ads/facebook/cities?search=Manhattan"); var content = await response.Content.ReadAsStringAsync(); Console.WriteLine(content); } } ``` ```go Go theme={"system"} package main import ( "fmt" "io/ioutil" "net/http" ) func main() { client := &http.Client{} req, _ := http.NewRequest("GET", "https://api.ayrshare.com/api/ads/facebook/cities?search=Manhattan", nil) req.Header.Add("Authorization", "Bearer API_KEY") resp, _ := client.Do(req) body, _ := ioutil.ReadAll(resp.Body) fmt.Println(string(body)) } ``` ```java Java theme={"system"} import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; public class GetAdCities { public static void main(String[] args) throws Exception { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("https://api.ayrshare.com/api/ads/facebook/cities?search=Manhattan")) .header("Authorization", "Bearer API_KEY") .GET() .build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); System.out.println(response.body()); } } ``` ```ruby Ruby theme={"system"} require 'net/http' require 'json' uri = URI('https://api.ayrshare.com/api/ads/facebook/cities?search=Manhattan') req = Net::HTTP::Get.new(uri) req['Authorization'] = 'Bearer API_KEY' res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) } puts res.body ``` ```json 200: Success theme={"system"} { "status": "success", "cities": [ { "key": "2447439", "name": "Manhattan", "type": "city", "countryCode": "US", "countryName": "United States", "region": "Kansas", "regionId": 3859, "supportsRegion": true, "supportsCity": true }, { "key": "2703980", "name": "Manhattan", "type": "subcity", "countryCode": "US", "countryName": "United States", "region": "New York", "regionId": 3875, "supportsRegion": true, "supportsCity": true, "geoHierarchyLevel": "SUBCITY", "geoHierarchyName": "BOROUGH" } ], "count": 2 } ``` ```json 414: Location metadata error theme={"system"} { "action": "ads", "status": "error", "code": 414, "message": "Error getting ad location metadata. Please try again." } ``` # History Source: https://www.ayrshare.com/docs/apis/ads/facebook/get-ad-history GET /ads/facebook/history Retrieve daily Facebook ad spend and performance metrics for ads created with Boost Post, with goal-specific results, aggregated totals, and date filters. Retrieve detailed daily ad spend and performance metrics for your Facebook ads. This endpoint provides comprehensive analytics on your ads with daily breakdowns, goal-specific performance, and aggregated metrics. > [!TIP] > > The `history` array contains historical daily spend and performance metrics > for each ad. The data is as of 8 AM EST. If real-time spend data is needed or spend has not yet occurred, > please use the [ads endpoint](/docs/apis/ads/facebook/get-boosted-ads). > > The ad will **NOT** be returned if no spend data is available or spend has not yet occurred. > > When no ad has spend in the date range, the response is still a success: `history` is > empty and `warnings` holds one entry with code `401`.
  • Metrics are calculated from Facebook's reporting API and may have slight delays (up to 24 hours).
  • The `byGoalType` and `byGoalTitle` sections group performance metrics by goal objectives.
  • Use either `adId` or `postId` filters to narrow results to specific campaigns.
  • Each day's `spend` is in the ad account's currency, shown in `currency`. `spendUsd` is the same spend in US dollars.
  • Non-USD spend is converted to USD at that day's exchange rate from Open Exchange Rates. `totalSpend`, `cpm`, `cpp`, `cpc`, `costPerResult` and the goal `spend` totals are in US dollars.
  • `totalSpendByCurrency` gives the total spend in each currency before conversion. `unconvertedRows` counts the days not converted yet: they count at face value in `totalSpend`, and their `spendUsd` is `null`.
  • Spend from before October 2026 may show `currency: "USD"` even when the ad account uses another currency, and is counted in `totalSpend` as recorded.
  • The `goalPerformance` section highlights your best-performing ad objectives.
> [!NOTE] > > This endpoint only returns Facebook ads created via the [Boost Post](/docs/apis/ads/facebook/boost-post) > endpoint. Ads created in Facebook Ads Manager or other third-party tools are not returned. ## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` **Profile-Key** `string` [Profile Key](/docs/apis/overview#profile-key-format) of a User Profile. Format: `Profile-Key: PROFILE_KEY` ## Query Parameters **accountId** `string` Filter results to a specific account ID. **adId** `string` Filter results to a specific ad ID. **postId** `string` Filter results to ads associated with a specific [Ayrshare post ID](/docs/apis/overview#ayrshare-post-id). **fbPostId** `string` Filter results to ads associated with a specific [Facebook social post ID](/docs/apis/overview#social-post-id). **limit** `number` (default: 25) Retrieve a maximum of `limit` ads. Maximum is 300; higher values are capped at 300. **startDate** `string` (default: 30 days ago) Start date for the reporting period in ISO 8601 format (default: 30 days ago) **endDate** `string` (default: today) End date for the reporting period in ISO 8601 format (default: today) ```bash cURL theme={"system"} curl \ -H "Authorization: Bearer API_KEY" \ -X GET https://api.ayrshare.com/api/ads/facebook/history?accountId=1234567890 ``` ```javascript JavaScript theme={"system"} const API_KEY = "API_KEY"; fetch("https://api.ayrshare.com/api/ads/facebook/history?accountId=1234567890", { method: "GET", headers: { "Authorization": `Bearer ${API_KEY}` } }) .then((res) => res.json()) .then((json) => console.log(json)) .catch(console.error); ``` ```python Python theme={"system"} import requests headers = {'Authorization': 'Bearer API_KEY'} r = requests.get('https://api.ayrshare.com/api/ads/facebook/history', headers=headers) print(r.json()) ``` ```php PHP theme={"system"} "https://api.ayrshare.com/api/ads/facebook/history", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer API_KEY" ], ]); $response = curl_exec($curl); $err = curl_error($curl); curl_close($curl); if ($err) { echo "cURL Error: " . $err; } else { echo $response; } ?> ``` ```csharp C# theme={"system"} using System; using System.Net.Http; using System.Threading.Tasks; class Program { static async Task Main(string[] args) { var client = new HttpClient(); client.DefaultRequestHeaders.Add("Authorization", "Bearer API_KEY"); var response = await client.GetAsync("https://api.ayrshare.com/api/ads/facebook/history"); var content = await response.Content.ReadAsStringAsync(); Console.WriteLine(content); } } ``` ```go Go theme={"system"} package main import ( "fmt" "io/ioutil" "net/http" ) func main() { client := &http.Client{} req, _ := http.NewRequest("GET", "https://api.ayrshare.com/api/ads/facebook/history", nil) req.Header.Add("Authorization", "Bearer API_KEY") resp, err := client.Do(req) if err != nil { fmt.Println("Error:", err) return } defer resp.Body.Close() body, _ := ioutil.ReadAll(resp.Body) fmt.Println(string(body)) } ``` ```java Java theme={"system"} import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; public class GetAdSpend { public static void main(String[] args) { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("https://api.ayrshare.com/api/ads/facebook/history")) .header("Authorization", "Bearer API_KEY") .GET() .build(); try { HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); System.out.println(response.body()); } catch (Exception e) { e.printStackTrace(); } } } ``` ```ruby Ruby theme={"system"} require 'net/http' require 'json' require 'uri' uri = URI.parse('https://api.ayrshare.com/api/ads/facebook/history') request = Net::HTTP::Get.new(uri) request['Authorization'] = 'Bearer API_KEY' response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == 'https') do |http| http.request(request) end puts response.body ``` ```json 200: Success No Param theme={"system"} { "status": "success", "history": [ { "adId": "6683876017501", "adName": "API Post 1 - DE6gpw8kxlonHy6eb7L1 - 2025-03-30T17:11:26", "budgetDaily": 4, "budgetType": "daily", "currency": "USD", "deliveryStatus": "ACTIVE", "goal": { "description": "This goal seeks to increase engagement while ensuring that the ad reaches the maximum number of unique users. It balances visibility with engagement, showing the ad to as many different people as possible who may interact with it.", "title": "Get More Engagement", "type": "engagement" }, "isActive": true, "isComplete": false, "metrics": { "clicks": 3, "impressions": 1035, "reach": 1026, "frequency": 1.01, "ctr": 0, "cpm": 1.53, "cpp": 1.54, "cpc": 0 }, "network": "facebook", "postId": "DE6gpw8kxlonHy6eb7L1", "spend": 1.58, "spendDate": "2025-04-02T00:00:00.000Z", "spendUsd": 1.58, "status": "ACTIVE" }, { "adId": "120218167147110411", "adName": "API Post 2 - DE6gpw8kxlonHy6eb7L1 - 2025-03-30T16:41:15", "budgetDaily": 4, "budgetType": "daily", "currency": "USD", "deliveryStatus": "ACTIVE", "goal": { "description": "This goal seeks to increase engagement while ensuring that the ad reaches the maximum number of unique users. It balances visibility with engagement, showing the ad to as many different people as possible who may interact with it.", "title": "Get More Engagement", "type": "engagement" }, "isActive": true, "isComplete": false, "metrics": { "clicks": 4, "impressions": 1036, "reach": 1011, "frequency": 1.02, "ctr": 0, "cpm": 1.61, "cpp": 1.65, "cpc": 0 }, "network": "facebook", "postId": "DE6gpw8kxlonHy6eb7L1", "spend": 1.67, "spendDate": "2025-04-02T00:00:00.000Z", "spendUsd": 1.67, "status": "ACTIVE" } ], "dateRange": { "start": "2025-02-24T20:02:34.613Z", "end": "2025-03-26T20:02:34.613Z", "postId": "eSNkaAiFZVin6wm6f" }, "metrics": { "totalSpend": 3.25, "totalSpendByCurrency": { "USD": 3.25 }, "unconvertedRows": 0, "totalClicks": 7, "totalImpressions": 2071, "totalReach": 2052, "averageCTR": 0.1, "averageFrequency": 1.02, "cpm": 0.79, "cpp": 0.85, "cpc": 0.2, "costPerResult": 1.2, "byGoalType": { "engagement": { "spend": 7.62, "impressions": 9332, "clicks": 0, "reach": 8502, "count": 8, "ctr": 0, "cpm": 0.8165452207458208, "cpp": 0.8962597035991532, "cpc": 0 } }, "byGoalTitle": { "Get More Engagement": { "spend": 3.25, "impressions": 2071, "clicks": 7, "reach": 2052, "count": 1, "type": "engagement", "ctr": 0.1, "cpm": 0.79, "cpp": 0.85, "cpc": 0.2 } }, "goalPerformance": { "bestPerformingGoalType": { "type": "engagement", "clicks": 7, "spend": 3.25, "ctr": 0.1 }, "mostEfficientGoalType": { "type": "engagement", "cpc": 0.2, "spend": 3.25 } }, "count": 2, "firstDay": "2025-03-17T00:00:00.000Z", "lastDay": "2025-03-23T00:00:00.000Z" } } ``` ```json 200: Success by Ad ID theme={"system"} { "status": "success", "history": [ { "accountId": "274948332", "adId": "6683876017505", "adName": "API Post 13 - DE6gpw8kxlonHy6eb7Lo - 2025-03-30T17:11:26", "budgetDaily": 4, "budgetType": "", "currency": "USD", "deliveryStatus": "ACTIVE", "fbPostId": "106638148652329_672633202087926", "goal": { "description": "This goal seeks to increase engagement while ensuring that the ad reaches the maximum number of unique users. It balances visibility with engagement, showing the ad to as many different people as possible who may interact with it.", "title": "Get More Engagement", "type": "engagement" }, "isActive": true, "isComplete": false, "metrics": { "clicks": 2, "cpc": 0, "cpp": 1.6, "cpm": 1.55, "ctr": 0, "frequency": 1.03, "impressions": 452, "reach": 438 }, "network": "facebook", "postId": "DE6gpw8kxlonHy6eb7Lo", "spend": 0.7, "spendDate": "2025-04-03T00:00:00.000Z", "spendUsd": 0.7, "status": "ACTIVE" }, { "accountId": "274948332", "adId": "6683876017505", "adName": "API Post 13 - DE6gpw8kxlonHy6eb7Lo - 2025-03-30T17:11:26", "budgetDaily": 4, "budgetType": "daily", "currency": "USD", "deliveryStatus": "ACTIVE", "fbPostId": "106638148652329_672633202087926", "goal": { "description": "This goal seeks to increase engagement while ensuring that the ad reaches the maximum number of unique users. It balances visibility with engagement, showing the ad to as many different people as possible who may interact with it.", "title": "Get More Engagement", "type": "engagement" }, "isActive": true, "isComplete": false, "metrics": { "clicks": 1, "cpc": 0, "cpp": 1.5, "cpm": 1.5, "ctr": 0, "frequency": 1, "impressions": 2793, "reach": 2793 }, "network": "facebook", "postId": "DE6gpw8kxlonHy6eb7Lo", "spend": 4.19, "spendDate": "2025-04-02T00:00:00.000Z", "spendUsd": 4.19, "status": "ACTIVE" } ], "dateRange": { "start": "2025-03-04T18:49:18.159Z", "end": "2025-04-03T18:49:18.159Z", "adId": "6683876017505" }, "metrics": { "totalSpend": 4.89, "totalSpendByCurrency": { "USD": 4.89 }, "unconvertedRows": 0, "totalClicks": 3, "totalImpressions": 10553, "totalReach": 10339, "averageCTR": 0.03, "averageFrequency": 1.02, "cpm": 1.460248270633943, "cpp": 1.49047296643776, "cpc": 5.136666666666667, "costPerResult": 5.14, "byGoalType": { "engagement": { "spend": 4.89, "impressions": 10553, "clicks": 3, "reach": 10339, "count": 2, "ctr": 0.03, "cpm": 1.46, "cpp": 1.49, "cpc": 5.14 } }, "byGoalTitle": { "Get More Engagement": { "spend": 4.89, "impressions": 10553, "clicks": 3, "reach": 10339, "count": 2, "type": "engagement", "ctr": 0.03, "cpm": 1.5, "cpp": 1.49, "cpc": 5.14 } }, "goalPerformance": { "bestPerformingGoalType": { "type": "engagement", "clicks": 3, "spend": 4.89, "ctr": 0.03 }, "mostEfficientGoalType": { "type": "engagement", "cpc": 5.14, "spend": 4.89 } }, "count": 2, "firstDay": "2025-03-30T00:00:00.000Z", "lastDay": "2025-04-03T00:00:00.000Z" } } ``` ```json 200: No spend data theme={"system"} { "status": "success", "history": [], "dateRange": { "start": "2025-03-04T18:49:18.159Z", "end": "2025-04-03T18:49:18.159Z", "adId": "6683876017505" }, "metrics": { "totalSpend": 0, "totalSpendByCurrency": {}, "unconvertedRows": 0, "totalClicks": 0, "totalImpressions": 0, "totalReach": 0, "averageCTR": 0, "averageFrequency": 0, "cpm": 0, "cpp": 0, "cpc": 0, "costPerResult": 0, "byGoalType": {}, "byGoalTitle": {}, "goalPerformance": { "bestPerformingGoalType": null, "mostEfficientGoalType": null }, "daysCount": 0, "firstDay": null, "lastDay": null }, "warnings": [ { "action": "ads", "status": "error", "code": 401, "message": "No spend data found for the ad." } ] } ``` ```json 400: Ad spend error theme={"system"} { "action": "get ad spend", "status": "error", "code": 367, "message": "Error getting ad spend. Please verify you have an active ad account." } ``` # Insights Source: https://www.ayrshare.com/docs/apis/ads/facebook/get-ad-insights GET /ads/facebook/insights Pull live daily Facebook ad metrics for campaigns, ad sets or ads, split by age, gender, country or placement, with paging for long date ranges. Get live Facebook ad performance, one row per day, for your campaigns, ad sets or ads. Pick the metrics you want, split them by age, gender, country or placement, and page through long date ranges.
  • Numbers are read from Meta when you call, not from a nightly copy.
  • Use [History](/docs/apis/ads/facebook/get-ad-history) for the spend we bill. Use Insights for reporting and analysis.
  • All money values are in the ad account's currency, returned in `currency` on each row.
  • Campaigns must have been created through Ayrshare for this profile, and ad sets must be in such a campaign. An ad created through Ayrshare for another profile is refused. Other ad ids are read with this profile's linked Meta account, so Meta only returns ads that account can see.
  • One request covers one ad account. Ids from several ad accounts return code `101`, and so do several ad ids that Ayrshare has no record of. Ask for those one at a time.
## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` **Profile-Key** `string` [Profile Key](/docs/apis/overview#profile-key-format) of a User Profile. Format: `Profile-Key: PROFILE_KEY` ## Query Parameters **level** `string` *required* What each row reports on: `campaign`, `adSet` or `ad`. **ids** `string` Comma-separated campaign, ad set or ad ids, up to 50. Required for `level=campaign` and `level=adSet`. With `level=ad`, you can leave `ids` out and send `accountId` instead. This reports on the ads you created through Ayrshare for this profile in that ad account, not on every ad in it. It works for up to 50 such ads. For more, send their ids, or report by campaign. If none are found, the report is empty. **accountId** `string` The ad account id, with or without the `act_` prefix (`1234567890` or `act_1234567890`). Required with `level=ad` when `ids` is left out. **metrics** `string` Comma-separated metrics to return. Leave it out to get them all. See [Metrics](#metrics). **breakdowns** `string` Comma-separated breakdowns: `age`, `gender`, `country`, `placement`. Each day then has one row per value, or per combination with `age,gender`. Send one breakdown at a time, or `age,gender`; Meta refuses other mixes (such as `age,country`), so they return code `101`. **startDate** `string` (default: 30 days ago) First day, as `YYYY-MM-DD`. It can be up to 37 months ago. **endDate** `string` (default: today) Last day, as `YYYY-MM-DD`. **cursor** `string` The `nextCursor` from the previous response, to get the next page. ## Metrics | Metric | Type | Meaning | | - | - | - | | `spend` | number | Amount spent | | `impressions` | number | Times the ad was shown | | `reach` | number | People who saw the ad at least once | | `clicks` | number | Clicks | | `cpm` | number or `null` | Cost per 1,000 impressions | | `cpc` | number or `null` | Cost per click. `null` on a day with no clicks | | `ctr` | number or `null` | Click-through rate, in percent | | `actions` | array | Each action type Meta counted, with its count | | `conversionValue` | number | Total value of purchases | Facebook reports every metric in this table. One exception: Meta does not report `reach` for a report with `breakdowns` that starts more than 13 months ago, so `reach` is left out of those rows. On networks that don't report a metric, it is left out of the rows rather than returned as `0`, and asking for it returns code `101`. > [!NOTE] > > Meta counts some actions under more than one type, so don't add `actions` values together. For > example, `omni_purchase` already includes `purchase`. `conversionValue` counts each purchase once. ## Paging Each response returns up to 500 rows. If there are more, `nextCursor` is set. Send the same request again with `cursor` set to that value to get the next page. On the last page, `nextCursor` is `null`. ## Errors | Code | When | | - | - | | `101` | A parameter is missing or wrong, a breakdown or metric isn't supported, `startDate` is more than 37 months ago, more than 50 ids are sent, the ids are in more than one ad account, or Meta refuses the breakdown combination | | `400` | A campaign, ad set or ad isn't yours, or `accountId` isn't the ad account Ayrshare has on record for the ids you sent. For an ad set, `accountId` must also be the ad account Meta reports for it. For an ad with no Ayrshare record, `accountId` is not checked and the ad is read on its own | | `370` | Meta could not return the insights. Try again | ```bash cURL theme={"system"} curl \ -H "Authorization: Bearer API_KEY" \ -X GET "https://api.ayrshare.com/api/ads/facebook/insights?level=campaign&ids=120200000000000001&startDate=2026-09-01&endDate=2026-09-02" ``` ```javascript JavaScript theme={"system"} const API_KEY = "API_KEY"; const params = new URLSearchParams({ level: "campaign", ids: "120200000000000001", startDate: "2026-09-01", endDate: "2026-09-02", }); fetch(`https://api.ayrshare.com/api/ads/facebook/insights?${params}`, { method: "GET", headers: { Authorization: `Bearer ${API_KEY}` }, }) .then((res) => res.json()) .then((json) => console.log(json)) .catch(console.error); ``` ```python Python theme={"system"} import requests headers = {'Authorization': 'Bearer API_KEY'} params = { 'level': 'campaign', 'ids': '120200000000000001', 'startDate': '2026-09-01', 'endDate': '2026-09-02', } r = requests.get('https://api.ayrshare.com/api/ads/facebook/insights', headers=headers, params=params) print(r.json()) ``` ```json 200: Success theme={"system"} { "status": "success", "platform": "facebook", "level": "campaign", "startDate": "2026-09-01", "endDate": "2026-09-02", "insights": [ { "date": "2026-09-01", "level": "campaign", "id": "120200000000000001", "currency": "USD", "spend": 12.34, "impressions": 4000, "reach": 3100, "clicks": 80, "cpm": 3.085, "cpc": 0.15425, "ctr": 2, "actions": [ { "type": "link_click", "value": 75 }, { "type": "omni_purchase", "value": 3 } ], "conversionValue": 89.97 } ], "count": 1, "nextCursor": null } ``` ```json 200: Breakdowns theme={"system"} { "status": "success", "platform": "facebook", "level": "ad", "startDate": "2026-09-01", "endDate": "2026-09-01", "insights": [ { "date": "2026-09-01", "level": "ad", "id": "120210000000000001", "age": "25-34", "gender": "female", "currency": "EUR", "spend": 1.5, "impressions": 300 }, { "date": "2026-09-01", "level": "ad", "id": "120210000000000001", "age": "25-34", "gender": "male", "currency": "EUR", "spend": 1, "impressions": 200 } ], "count": 2, "nextCursor": null } ``` ```json 400: Not Yours theme={"system"} { "action": "ads", "status": "error", "code": 400, "message": "The ad is not found or not authorized." } ``` # Interests Source: https://www.ayrshare.com/docs/apis/ads/facebook/get-ad-interests GET /ads/facebook/interests Search Facebook ad targeting interests by keyword for interest IDs and audience size estimates to use in Boost Post, or narrow to a special ad category. Search for available Facebook ad targeting interests by keyword. Use this endpoint to look up interest IDs for the [Boost Post](/docs/apis/ads/facebook/boost-post) endpoint. This endpoint helps you discover interest-based targeting options for your Facebook ads, along with audience size estimates.
  • Interest IDs can be used in the `interests` array when boosting posts.
  • Results are cached for 10 minutes.
  • Use specific, relevant search terms for best results.
  • Facebook may return different results based on your ad account's industry and region.
  • The `audienceSizeLowerBound` and `audienceSizeUpperBound` fields in the JSON response provide an estimated range of audience size for the interest and it is useful for targeting.
  • Use `specialAdCategories` to return only the interests Meta allows for a [special ad category](/docs/apis/ads/facebook/boost-post#param-special-ad-categories) campaign. Filtered requests also require `accountId`.
## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` **Profile-Key** `string` [Profile Key](/docs/apis/overview#profile-key-format) of a User Profile. Format: `Profile-Key: PROFILE_KEY` ## Query Parameters **search** `string` *required* The search query to retrieve Facebook ad interests. Don't forget to escape the search query, for example `Rhythm%20and%20blues%20music`. **specialAdCategories** `array` Restrict the results to the interests Meta allows a [special ad category](/docs/apis/ads/facebook/boost-post#param-special-ad-categories) campaign to target. Use this before boosting a housing, employment, financial, or political post so you pick interest IDs the ad will actually be approved for. The following values are supported, matching the [Boost Post](/docs/apis/ads/facebook/boost-post#param-special-ad-categories) parameter of the same name:
  • `housing`
  • `employment`
  • `financial_product_services` — `financial_products_services` and `credit` are accepted as equivalents and resolve to the same category
  • `issues_elections_politics`
  • `none` — accepted for symmetry with [Boost Post](/docs/apis/ads/facebook/boost-post#param-special-ad-categories), where it means "not in a special ad category". It is sent to Meta as `regulated_categories=["NONE"]`: `accountId` is required and a successful response echoes `["NONE"]`. This does not guarantee the same results as an unfiltered search; a Meta rejection returns `code: 368`. Omit `specialAdCategories` entirely rather than sending `none` if you want an ordinary unfiltered search.
Values are case-insensitive. An unrecognized value is rejected with a `400` and `code: 101` listing the accepted values. That message is written in Meta's upper-case form and also names the alternative spellings above, so it reads `NONE, HOUSING, FINANCIAL_PRODUCTS_SERVICES, EMPLOYMENT, ISSUES_ELECTIONS_POLITICS, FINANCIAL_PRODUCT_SERVICES, CREDIT` — the same categories listed here, not extra ones. Boost Post returns the identical message. An empty value, as in `?specialAdCategories=`, is rejected the same way. Pass more than one category by repeating the parameter, for example `?search=nursing&specialAdCategories=employment&specialAdCategories=housing`. The bracketed form `?specialAdCategories[]=employment&specialAdCategories[]=housing` works as well. A comma-separated string such as `?specialAdCategories=employment,housing` is **not** supported and is rejected with a `400` and `code: 101`. All requested categories are sent to Meta in one search. Ayrshare returns Meta's results and does not compute or guarantee an intersection of independently eligible interests. Repeating a category, or sending two spellings of the same one, collapses to a single category. When you supply more than one category, the `specialAdCategories` field echoed back in the response is sorted alphabetically rather than returned in the order you sent it. Omit `specialAdCategories` entirely and the endpoint behaves exactly as it did before: an unfiltered search, no `accountId` needed, and the same response shape. **accountId** `string` Required when `specialAdCategories` is supplied, and ignored otherwise. The ID of the Facebook ad account the interests are being searched for, without the `act_` prefix — the same value [Boost Post](/docs/apis/ads/facebook/boost-post) takes. Retrieve it from the [ad accounts endpoint](/docs/apis/ads/facebook/get-ad-accounts). It must be numeric; a leading `act_` is accepted and stripped, and anything else is rejected with a `400` and `code: 101`. > [!WARNING] > > Meta only offers category-aware interest search on an ad account, so a request that supplies > `specialAdCategories` without `accountId` is rejected with a `400` and `code: 101` before any > search runs. A request with no `specialAdCategories` is unchanged and still needs no `accountId`. **limit** `number` (default: 100) Limit the number of ad interests returned. > [!NOTE] > > A filtered response echoes the applied categories back in a `specialAdCategories` field, so you > can confirm what the results were narrowed against. An unfiltered response omits that field > entirely rather than returning it empty. Filtered results also omit the `topic` field on each > interest, because Meta's category-aware search does not return it — read `name` instead. A search > with no eligible interests returns `200` with an empty `interests` array and `count: 0`, not an > error, and a filtered search never falls back to unfiltered results. ```bash cURL theme={"system"} curl \ -H "Authorization: Bearer API_KEY" \ -X GET https://api.ayrshare.com/api/ads/facebook/interests?search=Rhythm%20and%20blues%20music ``` ```javascript JavaScript theme={"system"} const API_KEY = "API_KEY"; fetch("https://api.ayrshare.com/api/ads/facebook/interests?search=Rhythm%20and%20blues%20music", { method: "GET", headers: { "Authorization": `Bearer ${API_KEY}` } }) .then((res) => res.json()) .then((json) => console.log(json)) .catch(console.error); ``` ```python Python theme={"system"} import requests headers = {'Authorization': 'Bearer API_KEY'} r = requests.get('https://api.ayrshare.com/api/ads/facebook/interests?search=Rhythm%20and%20blues%20music', headers=headers) print(r.json()) ``` ```php PHP theme={"system"} "https://api.ayrshare.com/api/ads/facebook/interests?search=Rhythm%20and%20blues%20music", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer API_KEY" ], ]); $response = curl_exec($curl); curl_close($curl); echo $response; ?> ``` ```csharp C# theme={"system"} using System; using System.Net.Http; using System.Threading.Tasks; class Program { static async Task Main() { var client = new HttpClient(); client.DefaultRequestHeaders.Add("Authorization", "Bearer API_KEY"); var response = await client.GetAsync("https://api.ayrshare.com/api/ads/facebook/interests?search=Rhythm%20and%20blues%20music"); var content = await response.Content.ReadAsStringAsync(); Console.WriteLine(content); } } ``` ```go Go theme={"system"} package main import ( "fmt" "io/ioutil" "net/http" ) func main() { client := &http.Client{} req, _ := http.NewRequest("GET", "https://api.ayrshare.com/api/ads/facebook/interests?search=Rhythm%20and%20blues%20music", nil) req.Header.Add("Authorization", "Bearer API_KEY") resp, _ := client.Do(req) body, _ := ioutil.ReadAll(resp.Body) fmt.Println(string(body)) } ``` ```java Java theme={"system"} import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; public class GetAdInterests { public static void main(String[] args) throws Exception { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("https://api.ayrshare.com/api/ads/facebook/interests?search=Rhythm%20and%20blues%20music")) .header("Authorization", "Bearer API_KEY") .GET() .build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); System.out.println(response.body()); } } ``` ```ruby Ruby theme={"system"} require 'net/http' require 'json' uri = URI('https://api.ayrshare.com/api/ads/facebook/interests?search=Rhythm%20and%20blues%20music') req = Net::HTTP::Get.new(uri) req['Authorization'] = 'Bearer API_KEY' res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) } puts res.body ``` ```json 200: Success theme={"system"} { "status": "success", "interests": [ { "id": "6003195554098", "name": "Rhythm and blues music", "topic": "News and entertainment", "audienceSizeLowerBound": 669180255, "audienceSizeUpperBound": 786955980 }, { "id": "6003470511564", "name": "Do it yourself (DIY)", "topic": "Hobbies and activities", "audienceSizeLowerBound": 418387661, // Lower bound of audience size "audienceSizeUpperBound": 492023890 // Upper bound of audience size }, { "id": "6002926036121", "name": "Italy", "topic": "Travel, places and events", "audienceSizeLowerBound": 279313350, // Lower bound of audience size "audienceSizeUpperBound": 328472500 // Upper bound of audience size } ], "count": 3, "lastUpdated": "2025-03-27T01:01:38.547Z", "nextUpdate": "2025-03-27T01:12:38.547Z" } ``` ```json 200: Filtered by special ad category theme={"system"} { "status": "success", "interests": [ { "id": "6003269553527", "name": "Nursing", "audienceSizeLowerBound": 91340120, "audienceSizeUpperBound": 107420310 }, { "id": "6003204427551", "name": "Health care", "audienceSizeLowerBound": 412880440, "audienceSizeUpperBound": 485610770 } ], "count": 2, "specialAdCategories": ["EMPLOYMENT"], "lastUpdated": "2026-09-11T01:01:38.547Z", "nextUpdate": "2026-09-11T01:12:38.547Z" } ``` ```json 400: Ad interests error theme={"system"} { "action": "get ad interests", "status": "error", "code": 368, "message": "Error getting ad interests. Please try again or contact us if the issue persists." } ``` # Regions Source: https://www.ayrshare.com/docs/apis/ads/facebook/get-ad-regions GET /ads/facebook/regions Search Facebook regions such as states and provinces for ad targeting, or call without parameters to list every country that supports region targeting. Search for regions (states, provinces, etc.) to be used for targeting when boosting an ad.
  • Regions use Facebook's `key` identifier. Use this `key` when boosting ads with region targeting.
  • If a returned region has supportsRegion true, you can target this region. If supportsCity is true, the region has city codes.
  • To get all countries that support region targeting, call this endpoint without query parameters.
  • Results may vary based on Facebook data availability.
## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` **Profile-Key** `string` [Profile Key](/docs/apis/overview#profile-key-format) of a User Profile. Format: `Profile-Key: PROFILE_KEY` ## Query Parameters **search** `string` The search string to filter regions. Provide a partial or full region name. **limit** `number` (default: 25) Maximum number of regions to return. Only used with `search`. Without `search`, up to 1000 regions are returned. ```bash cURL theme={"system"} # All countries that support region targeting curl \ -H "Authorization: Bearer API_KEY" \ -X GET "https://api.ayrshare.com/api/ads/facebook/regions" ``` ```bash cURL theme={"system"} curl \ -H "Authorization: Bearer API_KEY" \ -X GET "https://api.ayrshare.com/api/ads/facebook/regions?search=al" ``` ```javascript JavaScript theme={"system"} const API_KEY = "API_KEY"; fetch("https://api.ayrshare.com/api/ads/facebook/regions?search=al", { method: "GET", headers: { "Authorization": `Bearer ${API_KEY}` } }) .then((res) => res.json()) .then((json) => console.log(json)) .catch(console.error); ``` ```python Python theme={"system"} import requests headers = {'Authorization': 'Bearer API_KEY'} r = requests.get('https://api.ayrshare.com/api/ads/facebook/regions?search=al', headers=headers) print(r.json()) ``` ```php PHP theme={"system"} "https://api.ayrshare.com/api/ads/facebook/regions?search=al", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer API_KEY" ], ]); $response = curl_exec($curl); curl_close($curl); echo $response; ?> ``` ```csharp C# theme={"system"} using System; using System.Net.Http; using System.Threading.Tasks; class Program { static async Task Main() { var client = new HttpClient(); client.DefaultRequestHeaders.Add("Authorization", "Bearer API_KEY"); var response = await client.GetAsync("https://api.ayrshare.com/api/ads/facebook/regions?search=al"); var content = await response.Content.ReadAsStringAsync(); Console.WriteLine(content); } } ``` ```go Go theme={"system"} package main import ( "fmt" "io/ioutil" "net/http" ) func main() { client := &http.Client{} req, _ := http.NewRequest("GET", "https://api.ayrshare.com/api/ads/facebook/regions?search=al", nil) req.Header.Add("Authorization", "Bearer API_KEY") resp, _ := client.Do(req) body, _ := ioutil.ReadAll(resp.Body) fmt.Println(string(body)) } ``` ```java Java theme={"system"} import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; public class GetAdRegions { public static void main(String[] args) throws Exception { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("https://api.ayrshare.com/api/ads/facebook/regions?search=al")) .header("Authorization", "Bearer API_KEY") .GET() .build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); System.out.println(response.body()); } } ``` ```ruby Ruby theme={"system"} require 'net/http' require 'json' uri = URI('https://api.ayrshare.com/api/ads/facebook/regions?search=al') req = Net::HTTP::Get.new(uri) req['Authorization'] = 'Bearer API_KEY' res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) } puts res.body ``` ```json 200: Success theme={"system"} { "status": "success", "regions": [ { "key": "3843", "name": "Alabama", "type": "region", "countryCode": "US", "countryName": "United States", "supportsRegion": true, "supportsCity": true }, { "key": "3844", "name": "Alaska", "type": "region", "countryCode": "US", "countryName": "United States", "supportsRegion": true, "supportsCity": true } ], "count": 2 } ``` ```json 414: Location metadata error theme={"system"} { "action": "ads", "status": "error", "code": 414, "message": "Error getting ad location metadata. Please try again." } ``` # Boosted Ads Source: https://www.ayrshare.com/docs/apis/ads/facebook/get-boosted-ads GET /ads/facebook/ads Retrieve boosted Facebook ads by account, ad, or post ID, with status, Meta review results, current spend, real-time metrics, and shareable preview links. Retrieve ads that have been boosted for a specific Facebook post. This boosted ads endpoint provides detailed information about the boosted ads, including status, current spend, performance metrics (analytics), and preview links.
  • The metrics are real-time totals for the ads. If you need historical data, use the [get ad history endpoint](/docs/apis/ads/facebook/get-ad-history).
  • `review` is Meta's review of the ad: `status` is `pending` while Meta reviews it, `rejected` when Meta disapproved it or left policy feedback on it, and `approved` when the ad is active or pre-approved. `reasons` holds Meta's explanations, or the policy's name when Meta gives no text, and is empty when there is no feedback. `review` is `null` when neither the ad's status nor any feedback shows the review, for example an ad paused before review.
  • The `previewLink` URL allows you share the preview ad with your colleagues, so that they will see the ad for 24-hours across the various Facebook formats. Your colleagues' views of the ad will not be counted towards your ad spend.
## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` **Profile-Key** `string` [Profile Key](/docs/apis/overview#profile-key-format) of a User Profile. Format: `Profile-Key: PROFILE_KEY` ## Query Parameters **accountId** `string` *required* Retrieve all ads under the account ID. The ad account id, with or without the `act_` prefix (`1234567890` or `act_1234567890`). Required if `adId`, `fbPostId`, or `postId` is not set. **adId** `string` *required* Retrieve an ad with the ad ID. Required if `accountId`, `fbPostId`, or `postId` is not set. **fbPostId** `string` *required* Retrieve ads with the [Facebook social post ID](/docs/apis/overview#social-post-id). Required if `accountId`, `adId`, or `postId` is not set. **limit** `number` (default: 25) Retrieve a maximum of `limit` ads. Maximum is 300; higher values are capped at 300. **postId** `string` *required* Retrieve ads with the Ayrshare [post ID](/docs/apis/overview#ayrshare-post-id). Required if `accountId`, `adId`, or `fbPostId` is not set. **status** `string` (default: all) Filter ads by status of the ad. Valid values are:
  • `active`
  • `paused`
  • `deleted`
  • `archived`
> [!NOTE] > > An ad boosted with a [`callToAction`](/docs/apis/ads/facebook/boost-post#param-call-to-action) carries > that object back on `ad.callToAction`, with `type` in Meta's upper-case form. The key is omitted > entirely for an ad that has no call-to-action button. A button that opens a chat also carries its > `destination`. The examples below use the `engagement` goal with no button. A link button needs > the `traffic` goal, and a [chat button](/docs/apis/ads/facebook/boost-post#message-destination) needs > `engagement` or `interactions`. A chat button boosted with `interactions` is listed with the > `engagement` goal when you filter by campaign. ```bash cURL theme={"system"} curl \ -H "Authorization: Bearer API_KEY" \ -X GET https://api.ayrshare.com/api/ads/facebook/ads?adId=1234567890 ``` ```javascript JavaScript theme={"system"} const API_KEY = "API_KEY"; fetch("https://api.ayrshare.com/api/ads/facebook/ads?adId=1234567890", { method: "GET", headers: { "Authorization": `Bearer ${API_KEY}` } }) .then((res) => res.json()) .then((json) => console.log(json)) .catch(console.error); ``` ```python Python theme={"system"} import requests headers = {'Authorization': 'Bearer API_KEY'} r = requests.get('https://api.ayrshare.com/api/ads/facebook/ads?adId=1234567890', headers=headers) print(r.json()) ``` ```php PHP theme={"system"} true, CURLOPT_HTTPHEADER => $headers ]); $response = curl_exec($curl); if ($response === false) { echo 'Curl error: ' . curl_error($curl); } else { echo json_encode(json_decode($response), JSON_PRETTY_PRINT); } curl_close($curl); ``` ```csharp C# theme={"system"} using System; using System.Net.Http; using System.Threading.Tasks; namespace HistoryGETRequest_csharp { class History { static async Task Main(string[] args) { string API_KEY = "API_KEY"; string url = "https://api.ayrshare.com/api/ads/facebook/ads?adId=1234567890"; using (var client = new HttpClient()) { client.DefaultRequestHeaders.Add("Authorization", "Bearer " + API_KEY); try { var response = await client.GetStringAsync(url); Console.WriteLine(response); } catch (HttpRequestException ex) { Console.WriteLine($"Error: {ex.Message}"); } } } } } ``` ```json 200: Success with Ad ID theme={"system"} { "status": "success", "ads": [ { "postId": "DE6gpw8kxlonHy6eb721", "ad": { "accountId": "274948331", "adId": "6683876017501", "budgetRemaining": 0, "created": "2025-03-30T10:11:34-0700", "creativeId": "1033490148629541", "dailyBudget": 0, "deliveryStatus": "ACTIVE", "endDate": "2025-04-03T22:30:00.000Z", "fbPostId": "106638148652329_672633202087922", "goal": { "title": "Get More Engagement", "description": "This goal seeks to increase engagement while ensuring that the ad reaches the maximum number of unique users. It balances visibility with engagement, showing the ad to as many different people as possible who may interact with it.", "type": "engagement" }, "isComplete": false, "lifetimeBudget": 0, "metrics": { "spend": 9.21, "impressions": 6495, "reach": 6272, "clicks": 3, "ctr": 0.046189, "cpm": 1.418014, "cpp": 1.468431, "frequency": 1.035555, "uniqueClicks": 3, "uniqueCtr": 0.047832, "costPerUniqueClick": 3.07, "inlineLinkClicks": 0, "costPerInlineLinkClick": 0, "outboundClicks": 0, "costPerOutboundClick": 0, "websiteCtr": [], "accountCurrency": "USD", "accountName": "John Doe", "accountId": "274948331" }, "name": "API Post - DE6gpw8kxlonHy6eb7L1 - 2025-03-30T17:11:26", "previewLink": "https://fb.me/22Cn31wXlzhxOC1", "review": { "reasons": [], "status": "approved" }, "spend": 9.21, "startDate": "2025-03-30T17:11:32.000Z", "status": "ACTIVE", "targeting": { "ageMax": 65, "ageMin": 18, "geoLocations": { "countries": [ "US" ], "locationTypes": [ "home", "recent" ] }, "interests": [ { "id": "6003195554098", "name": "Rhythm and blues music" } ] } } } ], "count": 1 } ``` ```json 200: Success with Account ID theme={"system"} { "status": "success", "ads": [ { "postId": "DE6gpw8kxlonHy6eb7Lo", "ad": { "accountId": "274948331", "adId": "6683876017501", "budgetRemaining": 0, "created": "2025-03-30T10:11:34-0700", "creativeId": "1033490148629541", "dailyBudget": 0, "deliveryStatus": "ACTIVE", "endDate": "2025-04-03T22:30:00.000Z", "fbPostId": "106638148652329_672633202087921", "goal": { "title": "Get More Engagement", "description": "This goal seeks to increase engagement while ensuring that the ad reaches the maximum number of unique users. It balances visibility with engagement, showing the ad to as many different people as possible who may interact with it.", "type": "engagement" }, "isComplete": false, "lifetimeBudget": 0, "metrics": { "spend": 9.21, "impressions": 6495, "reach": 6272, "clicks": 3, "ctr": 0.046189, "cpm": 1.418014, "cpp": 1.468431, "frequency": 1.035555, "uniqueClicks": 3, "uniqueCtr": 0.047832, "costPerUniqueClick": 3.07, "inlineLinkClicks": 0, "costPerInlineLinkClick": 0, "outboundClicks": 0, "costPerOutboundClick": 0, "websiteCtr": [], "accountCurrency": "USD", "accountName": "John Doe", "accountId": "274948331" }, "name": "API Post - DE6gpw8kxlonHy6eb7Lo - 2025-03-30T17:11:26", "previewLink": "https://fb.me/22Cn31wXlzhxOC1", "review": { "reasons": [], "status": "approved" }, "spend": 9.21, "startDate": "2025-03-30T17:11:32.000Z", "status": "ACTIVE", "targeting": { "ageMax": 65, "ageMin": 18, "geoLocations": { "countries": ["US"], "locationTypes": ["home", "recent"] }, "interests": [ { "id": "6003195554098", "name": "Rhythm and blues music" } ] } } }, { "ad": { "accountId": "274948331", "adId": "6685225314301", "budgetRemaining": 0, "created": "2025-03-30T18:36:39-0700", "creativeId": "628434386637341", "dailyBudget": 0, "deliveryStatus": "PAUSED", "endDate": "2025-04-03T22:30:00.000Z", "fbPostId": "106638148652329_672633202087921", "goal": { "title": "Get More Engagement", "description": "This goal seeks to increase engagement while ensuring that the ad reaches the maximum number of unique users. It balances visibility with engagement, showing the ad to as many different people as possible who may interact with it.", "type": "engagement" }, "isComplete": false, "lifetimeBudget": 0, "metrics": {}, "name": "API Post 14 - 106638148652329_672633202087926 - 2025-03-31T01:36:32", "previewLink": "https://fb.me/2nPlTIZEpzrCnY1", "review": null, "spend": 0, "startDate": "2025-03-31T01:36:37.000Z", "status": "PAUSED", "targeting": { "ageMax": 65, "ageMin": 18, "geoLocations": { "countries": ["US"], "locationTypes": ["home", "recent"] }, "interests": [ { "id": "6003195554098", "name": "Rhythm and blues music" } ] } } } ], "count": 2 } ``` ```json 400: Submitted ads retrieval error theme={"system"} { "action": "get ads", "status": "error", "code": 370, "message": "Error getting ads. Please try again or contact us if the issue persists." } ``` # DSA Recommendations Source: https://www.ayrshare.com/docs/apis/ads/facebook/get-dsa-recommendations GET /ads/facebook/dsaRecommendations Get Digital Services Act (DSA) beneficiary and payor recommendations for your Facebook ad account, required when boosting posts for ads that target the EU. Get Digital Services Act (DSA) recommendations for your Facebook ad account. As part of the requirements set forth by the European Union (EU) Digital Services Act (DSA), [Facebook requires ads targeting](https://www.facebook.com/business/help/605021638170961) any part of the EU to provide string values defining the beneficiary and payor of the ad being created This endpoint provides a list of recommended DSA beneficiaries and payors that can be used when boosting posts. These fields may be used in [`dsaBeneficiary`](/docs/apis/ads/facebook/boost-post#param-dsa-beneficiary) and [`dsaPayor`](/docs/apis/ads/facebook/boost-post#param-dsa-payor).
  • Outputs a list of strings (maximum 25) that Facebook has identified to likely be the beneficiary/payor, based on recent activity of the ad account.
  • These recommendations can be used as values for the `dsaBeneficiary` and `dsaPayor` parameters in the [Boost Post](/docs/apis/ads/facebook/boost-post) endpoint.
  • Both `dsaBeneficiary` and `dsaPayor` must be set together if either is provided when boosting posts.
## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` **Profile-Key** `string` [Profile Key](/docs/apis/overview#profile-key-format) of a User Profile. Format: `Profile-Key: PROFILE_KEY` ## Query Parameters **accountId** `string` *required* The ID of the Facebook ad account to get DSA recommendations for. The account ID can be retrieved from the [ad accounts endpoint](/docs/apis/ads/facebook/get-ad-accounts). ```bash cURL theme={"system"} curl \ -H "Authorization: Bearer API_KEY" \ -X GET https://api.ayrshare.com/api/ads/facebook/dsaRecommendations?accountId=1234567890 ``` ```javascript JavaScript theme={"system"} const API_KEY = "API_KEY"; fetch("https://api.ayrshare.com/api/ads/facebook/dsaRecommendations?accountId=1234567890", { method: "GET", headers: { "Authorization": `Bearer ${API_KEY}` } }) .then((res) => res.json()) .then((json) => console.log(json)) .catch(console.error); ``` ```python Python theme={"system"} import requests headers = {'Authorization': 'Bearer API_KEY'} r = requests.get('https://api.ayrshare.com/api/ads/facebook/dsaRecommendations?accountId=1234567890', headers=headers) print(r.json()) ``` ```php PHP theme={"system"} "https://api.ayrshare.com/api/ads/facebook/dsaRecommendations?accountId=1234567890", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer API_KEY" ], ]); $response = curl_exec($curl); curl_close($curl); echo $response; ?> ``` ```csharp C# theme={"system"} using System; using System.Net.Http; using System.Threading.Tasks; class Program { static async Task Main() { var client = new HttpClient(); client.DefaultRequestHeaders.Add("Authorization", "Bearer API_KEY"); var response = await client.GetAsync("https://api.ayrshare.com/api/ads/facebook/dsaRecommendations?accountId=1234567890"); var content = await response.Content.ReadAsStringAsync(); Console.WriteLine(content); } } ``` ```go Go theme={"system"} package main import ( "fmt" "io/ioutil" "net/http" ) func main() { client := &http.Client{} req, _ := http.NewRequest("GET", "https://api.ayrshare.com/api/ads/facebook/dsaRecommendations?accountId=1234567890", nil) req.Header.Add("Authorization", "Bearer API_KEY") resp, _ := client.Do(req) body, _ := ioutil.ReadAll(resp.Body) fmt.Println(string(body)) } ``` ```java Java theme={"system"} import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; public class GetDsaRecommendations { public static void main(String[] args) throws Exception { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("https://api.ayrshare.com/api/ads/facebook/dsaRecommendations?accountId=1234567890")) .header("Authorization", "Bearer API_KEY") .GET() .build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); System.out.println(response.body()); } } ``` ```ruby Ruby theme={"system"} require 'net/http' require 'json' uri = URI('https://api.ayrshare.com/api/ads/facebook/dsaRecommendations?accountId=1234567890') req = Net::HTTP::Get.new(uri) req['Authorization'] = 'Bearer API_KEY' res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) } puts res.body ``` ```json 200: Success theme={"system"} { "status": "success", "dsaRecommendations": [ "Beneficiary 1", "Beneficiary 2", "Beneficiary 3" ], "count": 1 } ``` ```json 413: DSA Recommendations Error theme={"system"} { "action": "ads", "status": "error", "code": 413, "message": "There was an error getting the DSA recommendations. Please try again." } ``` # Update Ad Source: https://www.ayrshare.com/docs/apis/ads/facebook/put-ad-update PUT /ads/facebook/ads Set a Facebook boosted ad to active, paused, deleted, or archived by adId, and see how status changes apply to boosts attached to an existing campaign. Update the status of a boosted ad to make it active, paused, deleted, or archived. * For a boost added to your own campaign or ad set with `attachTo`, the new status applies only to what the boost created: the ad, and the ad set when the boost created it. The campaign or ad set you named in `attachTo` keeps its own status. The ad runs only while the campaign and ad set above it are active. See [Boost into an Existing Campaign or Ad Set](/docs/apis/ads/facebook/boost-post#attach-to). * An ad that Ayrshare has no record of can be paused, deleted or archived, but not set to `active`. That request returns a `400`. Contact support with the ad id. ## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` **Profile-Key** `string` [Profile Key](/docs/apis/overview#profile-key-format) of a User Profile. Format: `Profile-Key: PROFILE_KEY` ## Body Parameters **adId** `string` *required* The boosted ad ID to update. **status** `string` *required* The status of the ad. Values: `active`, `paused`, `deleted`, or `archived`. ```bash cURL theme={"system"} curl \ -H "Authorization: Bearer API_KEY" \ -H "Content-Type: application/json" \ -X PUT https://api.ayrshare.com/api/ads/facebook/ads \ -d '{"adId": "1234567890", "status": "active"}' ``` ```javascript JavaScript theme={"system"} const API_KEY = "API_KEY"; fetch("https://api.ayrshare.com/api/ads/facebook/ads", { method: "PUT", headers: { "Authorization": `Bearer ${API_KEY}` }, body: JSON.stringify({ adId: "1234567890", status: "active" }) }) .then((res) => res.json()) .then((json) => console.log(json)) .catch(console.error); ``` ```python Python theme={"system"} import requests headers = {'Authorization': 'Bearer API_KEY'} r = requests.put('https://api.ayrshare.com/api/ads/facebook/ads', headers=headers, json={"adId": "1234567890", "status": "active"}) print(r.json()) ``` ```php PHP theme={"system"} "https://api.ayrshare.com/api/ads/facebook/ads", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer API_KEY" ], CURLOPT_CUSTOMREQUEST => "PUT", CURLOPT_POSTFIELDS => json_encode(["adId" => "1234567890", "status" => "active"]) ]); $response = curl_exec($curl); $err = curl_error($curl); curl_close($curl); if ($err) { echo "cURL Error: " . $err; } else { echo $response; } ?> ``` ```csharp C# theme={"system"} using System; using System.Net.Http; using System.Threading.Tasks; class Program { static async Task Main(string[] args) { var client = new HttpClient(); client.DefaultRequestHeaders.Add("Authorization", "Bearer API_KEY"); var response = await client.PutAsync("https://api.ayrshare.com/api/ads/facebook/ads", new StringContent(JsonConvert.SerializeObject(new { adId = "1234567890", status = "active" }), Encoding.UTF8, "application/json")); var content = await response.Content.ReadAsStringAsync(); Console.WriteLine(content); } } ``` ```go Go theme={"system"} package main import ( "fmt" "io/ioutil" "net/http" ) func main() { client := &http.Client{} req, _ := http.NewRequest("PUT", "https://api.ayrshare.com/api/ads/facebook/ads", nil) req.Header.Add("Authorization", "Bearer API_KEY") resp, err := client.Do(req) if err != nil { fmt.Println("Error:", err) return } defer resp.Body.Close() body, _ := ioutil.ReadAll(resp.Body) fmt.Println(string(body)) } ``` ```java Java theme={"system"} import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; public class GetAdSpend { public static void main(String[] args) { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("https://api.ayrshare.com/api/ads/facebook/ads")) .header("Authorization", "Bearer API_KEY") .PUT(HttpRequest.BodyPublishers.ofString(Json.stringify(new { adId = "1234567890", status = "active" }))) .build(); try { HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); System.out.println(response.body()); } catch (Exception e) { e.printStackTrace(); } } } ``` ```ruby Ruby theme={"system"} require 'net/http' require 'json' require 'uri' uri = URI.parse('https://api.ayrshare.com/api/ads/facebook/ads') request = Net::HTTP::Put.new(uri) request['Authorization'] = 'Bearer API_KEY' request.body = JSON.generate({ adId: "1234567890", status: "active" }) response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == 'https') do |http| http.request(request) end puts response.body ``` ```json 200: Success theme={"system"} { "status": "success", "adId": "6691084804905", "requestedStatus": "ACTIVE" } ``` ```json 400: Ad update error theme={"system"} { "action": "request", "status": "error", "code": 101, "message": "Missing or incorrect parameters. Please verify with the docs. https://www.ayrshare.com/docs/apis", "details": "Invalid status: actives. Status must be either 'active', 'paused', 'deleted', or 'archived'." } ``` # Accounts Source: https://www.ayrshare.com/docs/apis/ads/google/get-ad-accounts GET /ads/google/accounts List the Google Ads accounts you can create campaigns in, and see which linked accounts Google will not open and why. Get the Google Ads accounts you can create campaigns in. Use an account's `id` as `accountId` when you create a campaign.
  • Link Google Ads from the Social Accounts page first.
  • The Google user you link must have direct access to the ad account. Accounts you reach only through a manager account are not listed and cannot be used yet: add that user to the ad account itself. Manager accounts are not listed either: they cannot hold campaigns.
  • Every account comes back in one response, so `nextCursor` is always `null`.
## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` **Profile-Key** `string` [Profile Key](/docs/apis/overview#profile-key-format) of a User Profile. Format: `Profile-Key: PROFILE_KEY` ## Response `adAccounts` lists the accounts you can use, with each account's `id`, `name` and `currency`. If Google lists an account but will not open it, the response also has `unavailableAccounts`. Each entry has the account `id` and a `reason`. The field is missing when every account can be used. | `reason` | What it means | What to do | | - | - | - | | `notEnabled` | The account is cancelled, suspended, or not set up yet. | Finish setup or reactivate the account in Google Ads, then list the accounts again. Changes can take up to about 10 minutes to show. | | `noAccess` | The Google user you linked can see the account but has no access to its data. | Give that user access to the ad account in Google Ads, then list the accounts again. Changes can take up to about 10 minutes to show. | ```bash cURL theme={"system"} curl \ -H "Authorization: Bearer API_KEY" \ -X GET "https://api.ayrshare.com/api/ads/google/accounts" ``` ```javascript JavaScript theme={"system"} const API_KEY = "API_KEY"; fetch("https://api.ayrshare.com/api/ads/google/accounts", { method: "GET", headers: { "Authorization": `Bearer ${API_KEY}` } }) .then((res) => res.json()) .then((json) => console.log(json)) .catch(console.error); ``` ```python Python theme={"system"} import requests headers = { "Authorization": "Bearer API_KEY", } r = requests.get("https://api.ayrshare.com/api/ads/google/accounts", headers=headers) print(r.json()) ``` ```php PHP theme={"system"} "https://api.ayrshare.com/api/ads/google/accounts", CURLOPT_RETURNTRANSFER => true, CURLOPT_CUSTOMREQUEST => "GET", CURLOPT_HTTPHEADER => [ "Authorization: Bearer API_KEY" ] ]); $response = curl_exec($curl); $err = curl_error($curl); curl_close($curl); if ($err) { echo "cURL Error: " . $err; } else { echo $response; } ?> ``` ```csharp C# theme={"system"} using System; using System.Net.Http; using System.Threading.Tasks; class Program { static async Task Main(string[] args) { var client = new HttpClient(); client.DefaultRequestHeaders.Add("Authorization", "Bearer API_KEY"); var response = await client.GetAsync("https://api.ayrshare.com/api/ads/google/accounts"); var content = await response.Content.ReadAsStringAsync(); Console.WriteLine(content); } } ``` ```go Go theme={"system"} package main import ( "fmt" "io" "net/http" ) func main() { req, _ := http.NewRequest("GET", "https://api.ayrshare.com/api/ads/google/accounts", nil) req.Header.Add("Authorization", "Bearer API_KEY") resp, err := http.DefaultClient.Do(req) if err != nil { fmt.Println("Error:", err) return } defer resp.Body.Close() body, _ := io.ReadAll(resp.Body) fmt.Println(string(body)) } ``` ```java Java theme={"system"} import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; public class GetAdAccounts { public static void main(String[] args) throws Exception { HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("https://api.ayrshare.com/api/ads/google/accounts")) .header("Authorization", "Bearer API_KEY") .GET() .build(); HttpResponse response = HttpClient.newHttpClient() .send(request, HttpResponse.BodyHandlers.ofString()); System.out.println(response.body()); } } ``` ```ruby Ruby theme={"system"} require 'net/http' require 'uri' uri = URI.parse('https://api.ayrshare.com/api/ads/google/accounts') request = Net::HTTP::Get.new(uri) request['Authorization'] = 'Bearer API_KEY' response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http| http.request(request) end puts response.body ``` ```json 200: Success theme={"system"} { "status": "success", "adAccounts": [{ "id": "1234567890", "name": "Acme", "currency": "USD" }], "nextCursor": null, "unavailableAccounts": [{ "id": "2345678901", "reason": "notEnabled" }] } ``` # Insights Source: https://www.ayrshare.com/docs/apis/ads/google/get-ad-insights GET /ads/google/insights Pull live daily Google Ads metrics for your campaigns, ad groups or ads, with clicks, cost, conversions and conversion value. Get live Google Ads performance, one row per day, for the campaigns, ad groups or ads you created through Ayrshare.
  • Numbers are read from Google when you call, not from a nightly copy.
  • All money values are in the ad account's currency, returned in `currency` on each row.
  • Google leaves out days with no activity, so a range can return fewer rows than it has days.
  • Google adjusts a day's cost and conversions for a few days after it ends, so recent days can change between reads.
  • For a Performance Max campaign, use `level=campaign` with the campaign id. A Performance Max campaign has no ads of its own, so `level=ad` returns no rows.
## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` **Profile-Key** `string` [Profile Key](/docs/apis/overview#profile-key-format) of a User Profile. Format: `Profile-Key: PROFILE_KEY` ## Query Parameters **level** `string` *required* What each row reports on: `campaign` (the `campaignId` from Create), `adSet` (the `adGroupId` from Create) or `ad` (the `adId` from Create). **ids** `string` *required* Comma-separated campaign, ad group or ad ids, up to 50. They must all be in one ad account. **accountId** `string` The 10-digit Google Ads customer id, with or without hyphens (`123-456-7890`). Ad and ad group reports read the ad account you name here. Without it, an ad report searches every ad account your Google Ads link reaches, up to 25, and an ad group report works only when your link reaches one ad account. Name the account when your link reaches more than 25 ad accounts, or more than one for an ad group report. Otherwise the request answers `code: 101`. **metrics** `string` Comma-separated metrics to return. Leave it out to get them all. See [Metrics](#metrics). **startDate** `string` (default: 30 days ago) First day, as `YYYY-MM-DD`. Google keeps daily numbers for 37 months; an older `startDate` answers `code: 101`. **endDate** `string` (default: today) Last day, as `YYYY-MM-DD`. ## Metrics | Metric | Type | Meaning | | - | - | - | | `spend` | number | Amount spent | | `impressions` | number | Times the ad was shown | | `clicks` | number | Clicks | | `cpm` | number or `null` | Cost per 1,000 impressions. `null` on a day with no impressions | | `cpc` | number or `null` | Cost per click. `null` on a day with no clicks | | `ctr` | number or `null` | Click-through rate, in percent. `null` on a day with no impressions | | `actions` | number | Google's conversions, which can be fractional | | `conversionValue` | number | The value of those conversions | `actions` and `conversionValue` stay `0` until conversion tracking is set up in your Google Ads account. `reach` is not available, because Google reports no unique reach for Search, and `breakdowns` are not available on Google. Asking for either answers `code: 101`. ## Paging Every row comes back in one response, so `nextCursor` is always `null`. ## Errors | Code | When | | - | - | | `101` | A parameter is missing or wrong, a metric or breakdown isn't supported, `startDate` is more than 37 months ago, more than 50 ids are sent, the ids are in more than one ad account, or `accountId` is needed and missing | | `400` | A campaign, ad group or ad isn't yours, or `accountId` isn't the ad account Ayrshare has on record for the ids you sent | | `370` | Google could not return the insights. Try again | ```bash cURL theme={"system"} curl \ -H "Authorization: Bearer API_KEY" \ -X GET "https://api.ayrshare.com/api/ads/google/insights?level=campaign&ids=21534567890&startDate=2026-10-01&endDate=2026-10-07" ``` ```javascript JavaScript theme={"system"} const API_KEY = "API_KEY"; fetch("https://api.ayrshare.com/api/ads/google/insights?level=campaign&ids=21534567890&startDate=2026-10-01&endDate=2026-10-07", { method: "GET", headers: { "Authorization": `Bearer ${API_KEY}` } }) .then((res) => res.json()) .then((json) => console.log(json)) .catch(console.error); ``` ```python Python theme={"system"} import requests headers = { "Authorization": "Bearer API_KEY", } r = requests.get("https://api.ayrshare.com/api/ads/google/insights?level=campaign&ids=21534567890&startDate=2026-10-01&endDate=2026-10-07", headers=headers) print(r.json()) ``` ```php PHP theme={"system"} "https://api.ayrshare.com/api/ads/google/insights?level=campaign&ids=21534567890&startDate=2026-10-01&endDate=2026-10-07", CURLOPT_RETURNTRANSFER => true, CURLOPT_CUSTOMREQUEST => "GET", CURLOPT_HTTPHEADER => [ "Authorization: Bearer API_KEY" ] ]); $response = curl_exec($curl); $err = curl_error($curl); curl_close($curl); if ($err) { echo "cURL Error: " . $err; } else { echo $response; } ?> ``` ```csharp C# theme={"system"} using System; using System.Net.Http; using System.Threading.Tasks; class Program { static async Task Main(string[] args) { var client = new HttpClient(); client.DefaultRequestHeaders.Add("Authorization", "Bearer API_KEY"); var response = await client.GetAsync("https://api.ayrshare.com/api/ads/google/insights?level=campaign&ids=21534567890&startDate=2026-10-01&endDate=2026-10-07"); var content = await response.Content.ReadAsStringAsync(); Console.WriteLine(content); } } ``` ```go Go theme={"system"} package main import ( "fmt" "io" "net/http" ) func main() { req, _ := http.NewRequest("GET", "https://api.ayrshare.com/api/ads/google/insights?level=campaign&ids=21534567890&startDate=2026-10-01&endDate=2026-10-07", nil) req.Header.Add("Authorization", "Bearer API_KEY") resp, err := http.DefaultClient.Do(req) if err != nil { fmt.Println("Error:", err) return } defer resp.Body.Close() body, _ := io.ReadAll(resp.Body) fmt.Println(string(body)) } ``` ```java Java theme={"system"} import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; public class GetAdInsights { public static void main(String[] args) throws Exception { HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("https://api.ayrshare.com/api/ads/google/insights?level=campaign&ids=21534567890&startDate=2026-10-01&endDate=2026-10-07")) .header("Authorization", "Bearer API_KEY") .GET() .build(); HttpResponse response = HttpClient.newHttpClient() .send(request, HttpResponse.BodyHandlers.ofString()); System.out.println(response.body()); } } ``` ```ruby Ruby theme={"system"} require 'net/http' require 'uri' uri = URI.parse('https://api.ayrshare.com/api/ads/google/insights?level=campaign&ids=21534567890&startDate=2026-10-01&endDate=2026-10-07') request = Net::HTTP::Get.new(uri) request['Authorization'] = 'Bearer API_KEY' response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http| http.request(request) end puts response.body ``` ```json 200: Success theme={"system"} { "status": "success", "platform": "google", "level": "campaign", "startDate": "2026-10-01", "endDate": "2026-10-07", "insights": [ { "date": "2026-10-01", "level": "campaign", "id": "21534567890", "currency": "USD", "spend": 12.48, "impressions": 1830, "clicks": 41, "cpm": 6.82, "cpc": 0.3, "ctr": 2.2404, "actions": 3.5, "conversionValue": 140 } ], "count": 1, "nextCursor": null } ``` # Get Campaign Source: https://www.ayrshare.com/docs/apis/ads/google/get-campaign GET /ads/google/campaigns/{id} Read a Google Ads campaign's status and daily budget, and Google's review of each of its ads or assets. Get a Google Ads campaign you created through Ayrshare: its name, status, daily budget, and Google's review of each of its ads. ## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` **Profile-Key** `string` [Profile Key](/docs/apis/overview#profile-key-format) of a User Profile. Format: `Profile-Key: PROFILE_KEY` ## Path Parameters **id** `string` *required* The `campaignId` returned when you created the campaign. ## Response | Field | What it holds | | - | - | | `campaignId` | The campaign's id | | `name` | The campaign name | | `campaignStatus` | `active` or `paused` | | `platformStatus` | Google's own status: `ENABLED`, `PAUSED`, or `REMOVED` for a campaign deleted in Google Ads. A removed campaign keeps the last `campaignStatus` you set through Ayrshare | | `budget` | The daily budget: `amount`, `currency` (the ad account's currency) and `type` (`daily`) | | `ads` | Google's review of each ad (Search) or asset (Performance Max). See [Ad Review](#ad-review) | ## Ad Review Google reviews every new ad against its advertising policies, usually within one business day. Each row in `ads` holds: | Field | What it holds | | - | - | | `adId` | The ad's id, as returned by Create | | `approvalStatus` | Google's decision: `APPROVED`, `APPROVED_LIMITED` (shown with limits, for example not in some countries), `AREA_OF_INTEREST_ONLY` or `DISAPPROVED`. Until Google decides, it holds another value, such as `UNKNOWN` or `UNSPECIFIED` | | `reviewStatus` | Where the review stands, for example `REVIEW_IN_PROGRESS` or `REVIEWED` | | `policyTopics` | The policies behind a limit or disapproval: `[{ "topic": "ALCOHOL", "type": "PROHIBITED" }]` | A disapproved ad does not serve. Change it in Google Ads, or create a new campaign with compliant text. `ads` is left out when Google's review could not be read at that moment, or took more than a few seconds; read the campaign again later. Each night, Ayrshare also checks every review and sends an `ads` webhook with `type: "reviewChanged"` when Google's decision changes. See [Ads Action](/docs/apis/webhooks/actions#ads-action). ## Asset Review Google reviews each asset of a Performance Max campaign on its own. `ads` holds one row per asset, with the same fields as an [ad review](#ad-review) plus: | Field | What it holds | | - | - | | `adId` | The asset's key in this campaign: `asset:~~`, or `asset:~~` for a brand asset | | `assetId` | Google's id for the asset | | `fieldType` | Where it is used, for example `HEADLINE`, `MARKETING_IMAGE`, `SQUARE_MARKETING_IMAGE`, `BUSINESS_NAME` or `LOGO` | | `primaryStatus` | Whether it can serve, for example `ELIGIBLE` or `NOT_ELIGIBLE` | The business name, logos and landscape logos are brand assets, linked to the campaign rather than the asset group. They have a row here too, after the asset group rows, keyed by the campaign id: `asset:~~`, where `fieldType` is `BUSINESS_NAME`, `LOGO` or `LANDSCAPE_LOGO`. ## Errors | Code | When | | - | - | | `400` | The campaign is not yours, does not exist, or is no longer in the Google Ads account. Also when the Google user you linked cannot access the ad account, or Google Ads access was revoked. | | `370` | Google could not complete the request. Try again. | ```bash cURL theme={"system"} curl \ -H "Authorization: Bearer API_KEY" \ -X GET "https://api.ayrshare.com/api/ads/google/campaigns/21534567890" ``` ```javascript JavaScript theme={"system"} const API_KEY = "API_KEY"; fetch("https://api.ayrshare.com/api/ads/google/campaigns/21534567890", { method: "GET", headers: { "Authorization": `Bearer ${API_KEY}` } }) .then((res) => res.json()) .then((json) => console.log(json)) .catch(console.error); ``` ```python Python theme={"system"} import requests headers = { "Authorization": "Bearer API_KEY", } r = requests.get("https://api.ayrshare.com/api/ads/google/campaigns/21534567890", headers=headers) print(r.json()) ``` ```php PHP theme={"system"} "https://api.ayrshare.com/api/ads/google/campaigns/21534567890", CURLOPT_RETURNTRANSFER => true, CURLOPT_CUSTOMREQUEST => "GET", CURLOPT_HTTPHEADER => [ "Authorization: Bearer API_KEY" ] ]); $response = curl_exec($curl); $err = curl_error($curl); curl_close($curl); if ($err) { echo "cURL Error: " . $err; } else { echo $response; } ?> ``` ```csharp C# theme={"system"} using System; using System.Net.Http; using System.Threading.Tasks; class Program { static async Task Main(string[] args) { var client = new HttpClient(); client.DefaultRequestHeaders.Add("Authorization", "Bearer API_KEY"); var response = await client.GetAsync("https://api.ayrshare.com/api/ads/google/campaigns/21534567890"); var content = await response.Content.ReadAsStringAsync(); Console.WriteLine(content); } } ``` ```go Go theme={"system"} package main import ( "fmt" "io" "net/http" ) func main() { req, _ := http.NewRequest("GET", "https://api.ayrshare.com/api/ads/google/campaigns/21534567890", nil) req.Header.Add("Authorization", "Bearer API_KEY") resp, err := http.DefaultClient.Do(req) if err != nil { fmt.Println("Error:", err) return } defer resp.Body.Close() body, _ := io.ReadAll(resp.Body) fmt.Println(string(body)) } ``` ```java Java theme={"system"} import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; public class GetCampaign { public static void main(String[] args) throws Exception { HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("https://api.ayrshare.com/api/ads/google/campaigns/21534567890")) .header("Authorization", "Bearer API_KEY") .GET() .build(); HttpResponse response = HttpClient.newHttpClient() .send(request, HttpResponse.BodyHandlers.ofString()); System.out.println(response.body()); } } ``` ```ruby Ruby theme={"system"} require 'net/http' require 'uri' uri = URI.parse('https://api.ayrshare.com/api/ads/google/campaigns/21534567890') request = Net::HTTP::Get.new(uri) request['Authorization'] = 'Bearer API_KEY' response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http| http.request(request) end puts response.body ``` ```json 200: Search Campaign theme={"system"} { "status": "success", "platform": "google", "campaignId": "21534567890", "name": "Fall sale", "campaignStatus": "active", "platformStatus": "ENABLED", "budget": { "amount": 25, "currency": "USD", "type": "daily" }, "ads": [ { "adId": "71234567890", "approvalStatus": "APPROVED_LIMITED", "reviewStatus": "REVIEWED", "policyTopics": [{ "topic": "ALCOHOL", "type": "LIMITED" }] } ] } ``` ```json 200: Performance Max Campaign theme={"system"} { "status": "success", "platform": "google", "campaignId": "21534567891", "name": "Fall sale PMax", "campaignStatus": "paused", "platformStatus": "PAUSED", "budget": { "amount": 40, "currency": "USD", "type": "daily" }, "ads": [ { "adId": "asset:6512345678~31234567890~HEADLINE", "assetId": "31234567890", "fieldType": "HEADLINE", "primaryStatus": "ELIGIBLE", "approvalStatus": "APPROVED", "reviewStatus": "REVIEWED", "policyTopics": [] } ] } ``` ```json 400: Not Found theme={"system"} { "action": "ads", "status": "error", "code": 400, "message": "Campaign not found or you are not authorized to access it." } ``` # Google Performance Max Campaigns Source: https://www.ayrshare.com/docs/apis/ads/google/performance-max-campaigns POST /ads/google/campaigns Create a Google Ads Performance Max campaign in one request from text and image URLs, with a daily budget, one asset group and its assets built together. > [!WARNING] > > **Set up conversion tracking first.** Performance Max uses your conversions to decide where to show ads and how much to bid. Ayrshare does not block a campaign without it, but Google may limit its spend until conversion tracking is set up in your Google Ads account. Ayrshare does not set it up for you. When the account has none, the create response includes a `warnings` entry saying so. Create a Google Ads Performance Max campaign in one request. You send text and image URLs; Google mixes them into ads across Search, YouTube, Display, Discover, Gmail and Maps. Ayrshare builds the budget, the campaign, one asset group and all its assets together. If Google refuses any part, nothing is created. The request uses the shared [Ad Campaigns](/docs/apis/ads/campaigns) shape. Reading and changing a campaign, insights and errors work as on [Google Search Campaigns](/docs/apis/ads/google/search-campaigns). This page covers what is different.
  • Google ads are turned on per account. Contact us to enable them.
  • New campaigns start `paused` unless you send `status: "active"`.
  • The budget is per day, in the ad account's currency, and is never shared with other campaigns.
  • Spend in another currency is converted to USD for billing at that day's exchange rate from Open Exchange Rates. An account in a currency that cannot be converted answers `code: 101` before anything is created.
  • Video is not supported. Google may make videos from your images.
## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` **Profile-Key** `string` [Profile Key](/docs/apis/overview#profile-key-format) of a User Profile. Format: `Profile-Key: PROFILE_KEY` **Idempotency-Key** `string` *required* A new unique value, such as a UUID, for each new campaign. Repeating a request with the same key returns the first response. When a create fails before Ayrshare sends the campaign to Google (for example, an image that is the wrong size), the key is freed, so you can fix the request and retry with the same key. ## Body Parameters **type** `string` *required* `performanceMax`. **bidding** `object` *required* Performance Max takes only these strategies. Any other answers `code: 101`.
  • `maximizeConversions`: get the most conversions for the budget.
  • `targetCpa` with `targetCpa`: the same, aiming at that cost per conversion.
  • `maximizeConversionValue`: get the most conversion value for the budget.
  • `targetRoas` with `targetRoas`: the same, aiming at that return on ad spend, for example `3.5` for 350%.
**euPoliticalAdvertising** `boolean` *required* Google requires this declaration on every campaign. **targeting.locations** `string[]` Google location ids, for example `2840` for the United States. Leave it out to target every location. **platformOptions.google** `object` *required* **properties** **finalUrl** `string` *required* The `http` or `https` page the ads open. **assets** `object` *required* Your text and images. Each list's items must be different from each other. **properties** **headlines** `string[]` *required* 3 to 15, each up to 30 characters. **longHeadlines** `string[]` *required* 1 to 5, each up to 90 characters. **descriptions** `string[]` *required* 2 to 5, each up to 90 characters. At least one must be 60 characters or fewer. **businessName** `string` *required* One business name, up to 25 characters. **marketingImages** `string[]` *required* 1 to 20 landscape image URLs: 1.91:1, at least 600×314. **squareMarketingImages** `string[]` *required* 1 to 20 square image URLs: 1:1, at least 300×300. **logos** `string[]` *required* 1 to 5 square logo URLs: 1:1, at least 128×128. **landscapeLogos** `string[]` Up to 4 wide logo URLs: 4:1, at least 512×128. Google takes 5 logos and wide logos together at most. ### Images Ayrshare downloads each image from its URL and checks it before sending anything to Google:
  • GIF, JPG or PNG, up to 5120 KB each.
  • At least the minimum size above, with the aspect ratio of its field (within 1%).
  • All images together up to 45 MB.
  • The URL must be public. Private addresses are refused.
An image that fails answers `code: 101` naming it, for example `platformOptions.google.assets.logos[0]: the image is 100x100; use at least 128x128.` ```bash cURL theme={"system"} curl \ -H "Authorization: Bearer API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: 0b7a6f1c-2d4e-4c55-9a3e-2f8d1b7c9e11" \ -X POST https://api.ayrshare.com/api/ads/google/campaigns \ -d '{ "accountId": "123-456-7890", "name": "Fall sale PMax", "type": "performanceMax", "budget": { "amount": 40, "type": "daily" }, "bidding": { "strategy": "maximizeConversions" }, "targeting": { "locations": ["2840"] }, "euPoliticalAdvertising": false, "platformOptions": { "google": { "finalUrl": "https://example.com/fall-sale", "assets": { "headlines": ["Fall sale", "Shoes 30% off", "Free returns"], "longHeadlines": ["Every running shoe 30% off this week only"], "descriptions": ["Every shoe 30% off this week.", "Free returns on every order."], "businessName": "Acme Shoes", "marketingImages": ["https://example.com/images/fall-1200x628.png"], "squareMarketingImages": ["https://example.com/images/fall-1200x1200.png"], "logos": ["https://example.com/images/logo-512.png"] } } } }' ``` ```javascript JavaScript theme={"system"} const API_KEY = "API_KEY"; fetch("https://api.ayrshare.com/api/ads/google/campaigns", { method: "POST", headers: { "Authorization": `Bearer ${API_KEY}`, "Content-Type": "application/json", "Idempotency-Key": "0b7a6f1c-2d4e-4c55-9a3e-2f8d1b7c9e11" }, body: JSON.stringify({ "accountId": "123-456-7890", "name": "Fall sale PMax", "type": "performanceMax", "budget": { "amount": 40, "type": "daily" }, "bidding": { "strategy": "maximizeConversions" }, "targeting": { "locations": ["2840"] }, "euPoliticalAdvertising": false, "platformOptions": { "google": { "finalUrl": "https://example.com/fall-sale", "assets": { "headlines": ["Fall sale", "Shoes 30% off", "Free returns"], "longHeadlines": ["Every running shoe 30% off this week only"], "descriptions": ["Every shoe 30% off this week.", "Free returns on every order."], "businessName": "Acme Shoes", "marketingImages": ["https://example.com/images/fall-1200x628.png"], "squareMarketingImages": ["https://example.com/images/fall-1200x1200.png"], "logos": ["https://example.com/images/logo-512.png"] } } } }) }) .then((res) => res.json()) .then((json) => console.log(json)) .catch(console.error); ``` ```python Python theme={"system"} import requests headers = { "Authorization": "Bearer API_KEY", "Idempotency-Key": "0b7a6f1c-2d4e-4c55-9a3e-2f8d1b7c9e11", } payload = { "accountId": "123-456-7890", "name": "Fall sale PMax", "type": "performanceMax", "budget": { "amount": 40, "type": "daily" }, "bidding": { "strategy": "maximizeConversions" }, "targeting": { "locations": ["2840"] }, "euPoliticalAdvertising": False, "platformOptions": { "google": { "finalUrl": "https://example.com/fall-sale", "assets": { "headlines": ["Fall sale", "Shoes 30% off", "Free returns"], "longHeadlines": ["Every running shoe 30% off this week only"], "descriptions": ["Every shoe 30% off this week.", "Free returns on every order."], "businessName": "Acme Shoes", "marketingImages": ["https://example.com/images/fall-1200x628.png"], "squareMarketingImages": ["https://example.com/images/fall-1200x1200.png"], "logos": ["https://example.com/images/logo-512.png"] } } } } r = requests.post("https://api.ayrshare.com/api/ads/google/campaigns", headers=headers, json=payload) print(r.json()) ``` ```php PHP theme={"system"} "https://api.ayrshare.com/api/ads/google/campaigns", CURLOPT_RETURNTRANSFER => true, CURLOPT_CUSTOMREQUEST => "POST", CURLOPT_HTTPHEADER => [ "Authorization: Bearer API_KEY", "Content-Type: application/json", "Idempotency-Key: 0b7a6f1c-2d4e-4c55-9a3e-2f8d1b7c9e11" ], CURLOPT_POSTFIELDS => $payload ]); $response = curl_exec($curl); $err = curl_error($curl); curl_close($curl); if ($err) { echo "cURL Error: " . $err; } else { echo $response; } ?> ``` ```csharp C# theme={"system"} using System; using System.Net.Http; using System.Text; using System.Threading.Tasks; class Program { static async Task Main(string[] args) { var client = new HttpClient(); client.DefaultRequestHeaders.Add("Authorization", "Bearer API_KEY"); client.DefaultRequestHeaders.Add("Idempotency-Key", "0b7a6f1c-2d4e-4c55-9a3e-2f8d1b7c9e11"); var payload = """ { "accountId": "123-456-7890", "name": "Fall sale PMax", "type": "performanceMax", "budget": { "amount": 40, "type": "daily" }, "bidding": { "strategy": "maximizeConversions" }, "targeting": { "locations": ["2840"] }, "euPoliticalAdvertising": false, "platformOptions": { "google": { "finalUrl": "https://example.com/fall-sale", "assets": { "headlines": ["Fall sale", "Shoes 30% off", "Free returns"], "longHeadlines": ["Every running shoe 30% off this week only"], "descriptions": ["Every shoe 30% off this week.", "Free returns on every order."], "businessName": "Acme Shoes", "marketingImages": ["https://example.com/images/fall-1200x628.png"], "squareMarketingImages": ["https://example.com/images/fall-1200x1200.png"], "logos": ["https://example.com/images/logo-512.png"] } } } } """; var response = await client.PostAsync( "https://api.ayrshare.com/api/ads/google/campaigns", new StringContent(payload, Encoding.UTF8, "application/json")); var content = await response.Content.ReadAsStringAsync(); Console.WriteLine(content); } } ``` ```go Go theme={"system"} package main import ( "fmt" "io" "net/http" "strings" ) func main() { payload := `{ "accountId": "123-456-7890", "name": "Fall sale PMax", "type": "performanceMax", "budget": { "amount": 40, "type": "daily" }, "bidding": { "strategy": "maximizeConversions" }, "targeting": { "locations": ["2840"] }, "euPoliticalAdvertising": false, "platformOptions": { "google": { "finalUrl": "https://example.com/fall-sale", "assets": { "headlines": ["Fall sale", "Shoes 30% off", "Free returns"], "longHeadlines": ["Every running shoe 30% off this week only"], "descriptions": ["Every shoe 30% off this week.", "Free returns on every order."], "businessName": "Acme Shoes", "marketingImages": ["https://example.com/images/fall-1200x628.png"], "squareMarketingImages": ["https://example.com/images/fall-1200x1200.png"], "logos": ["https://example.com/images/logo-512.png"] } } } }` req, _ := http.NewRequest("POST", "https://api.ayrshare.com/api/ads/google/campaigns", strings.NewReader(payload)) req.Header.Add("Authorization", "Bearer API_KEY") req.Header.Add("Content-Type", "application/json") req.Header.Add("Idempotency-Key", "0b7a6f1c-2d4e-4c55-9a3e-2f8d1b7c9e11") resp, err := http.DefaultClient.Do(req) if err != nil { fmt.Println("Error:", err) return } defer resp.Body.Close() body, _ := io.ReadAll(resp.Body) fmt.Println(string(body)) } ``` ```java Java theme={"system"} import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; public class CreateCampaign { public static void main(String[] args) throws Exception { String payload = """ { "accountId": "123-456-7890", "name": "Fall sale PMax", "type": "performanceMax", "budget": { "amount": 40, "type": "daily" }, "bidding": { "strategy": "maximizeConversions" }, "targeting": { "locations": ["2840"] }, "euPoliticalAdvertising": false, "platformOptions": { "google": { "finalUrl": "https://example.com/fall-sale", "assets": { "headlines": ["Fall sale", "Shoes 30% off", "Free returns"], "longHeadlines": ["Every running shoe 30% off this week only"], "descriptions": ["Every shoe 30% off this week.", "Free returns on every order."], "businessName": "Acme Shoes", "marketingImages": ["https://example.com/images/fall-1200x628.png"], "squareMarketingImages": ["https://example.com/images/fall-1200x1200.png"], "logos": ["https://example.com/images/logo-512.png"] } } } } """; HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("https://api.ayrshare.com/api/ads/google/campaigns")) .header("Authorization", "Bearer API_KEY") .header("Content-Type", "application/json") .header("Idempotency-Key", "0b7a6f1c-2d4e-4c55-9a3e-2f8d1b7c9e11") .POST(HttpRequest.BodyPublishers.ofString(payload)) .build(); HttpResponse response = HttpClient.newHttpClient() .send(request, HttpResponse.BodyHandlers.ofString()); System.out.println(response.body()); } } ``` ```ruby Ruby theme={"system"} require 'net/http' require 'uri' uri = URI.parse('https://api.ayrshare.com/api/ads/google/campaigns') request = Net::HTTP::Post.new(uri) request['Authorization'] = 'Bearer API_KEY' request['Content-Type'] = 'application/json' request['Idempotency-Key'] = '0b7a6f1c-2d4e-4c55-9a3e-2f8d1b7c9e11' request.body = <<~JSON { "accountId": "123-456-7890", "name": "Fall sale PMax", "type": "performanceMax", "budget": { "amount": 40, "type": "daily" }, "bidding": { "strategy": "maximizeConversions" }, "targeting": { "locations": ["2840"] }, "euPoliticalAdvertising": false, "platformOptions": { "google": { "finalUrl": "https://example.com/fall-sale", "assets": { "headlines": ["Fall sale", "Shoes 30% off", "Free returns"], "longHeadlines": ["Every running shoe 30% off this week only"], "descriptions": ["Every shoe 30% off this week.", "Free returns on every order."], "businessName": "Acme Shoes", "marketingImages": ["https://example.com/images/fall-1200x628.png"], "squareMarketingImages": ["https://example.com/images/fall-1200x1200.png"], "logos": ["https://example.com/images/logo-512.png"] } } } } JSON response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http| http.request(request) end puts response.body ``` ```json 200: Campaign Created theme={"system"} { "status": "success", "platform": "google", "campaignId": "21534567891", "assetGroupId": "6512345678", "campaignStatus": "paused" } ``` ```json 200: Created, No Conversion Tracking theme={"system"} { "status": "success", "platform": "google", "campaignId": "21534567891", "assetGroupId": "6512345678", "campaignStatus": "paused", "warnings": [ "This Google Ads account has no conversion tracking. Performance Max optimizes for conversions, so Google may limit the campaign's spend until you set up conversion tracking in Google Ads." ] } ``` ```json 400: Image Too Small theme={"system"} { "action": "request", "status": "error", "code": 101, "message": "Missing or incorrect parameters. Please verify with the docs. https://www.ayrshare.com/docs/apis", "details": "platformOptions.google.assets.logos[0]: the image is 100x100; use at least 128x128." } ``` # Google Search Campaigns Source: https://www.ayrshare.com/docs/apis/ads/google/search-campaigns POST /ads/google/campaigns Create a Google Ads Search campaign with its budget, keywords, ad text and location targeting in one request Create a Google Ads Search campaign in one request. Ayrshare builds the budget, the campaign, one ad group, your keywords, one responsive search ad, and the location targeting together. If Google refuses any part, nothing is created. The request uses the shared [Ad Campaigns](/docs/apis/ads/campaigns) shape. This page covers what is specific to Google.
  • Google ads are turned on per account. Contact us to enable them.
  • Link Google Ads from the Social Accounts page first. `accountId` is the 10-digit Google Ads customer id, with or without hyphens (`123-456-7890`).
  • The Google user you link must have direct access to the ad account. Accounts you reach only through a manager account are not listed and cannot be used yet: add that user to the ad account itself. Manager accounts are not listed either: they cannot hold campaigns.
  • New campaigns start `paused` unless you send `status: "active"`.
  • The budget is per day, in the ad account's currency. Do not send `budget.currency`: it answers `code: 101`.
  • Spend in another currency is converted to USD for billing at that day's exchange rate from Open Exchange Rates. An account in a currency that cannot be converted answers `code: 101` before anything is created.
  • Ads show on Google Search only, not on search partners or the Display Network.
> [!NOTE] > > **Conversion tracking.** `maximizeConversions` only works when conversion tracking is set up in your Google Ads account. Ayrshare does not set it up for you. Without it, use `maximizeClicks` or `manualCpc`. ## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` **Profile-Key** `string` [Profile Key](/docs/apis/overview#profile-key-format) of a User Profile. Format: `Profile-Key: PROFILE_KEY` **Idempotency-Key** `string` *required* A new unique value, such as a UUID, for each new campaign. Repeating a request with the same key returns the first response and creates nothing new. When a create fails before Ayrshare sends the campaign to Google, the key is freed, so you can fix the request and retry with the same key. That covers a value Ayrshare refuses (such as an unsupported bidding strategy), an account in a currency that cannot be converted to USD, and any failure of the account check Ayrshare makes first, including Google refusing it. A create that fails once the campaign was sent to Google keeps its answer: retry it with a new key. ## Body Parameters The shared fields work as described on [Ad Campaigns](/docs/apis/ads/campaigns). On Google: **type** `string` *required* `search`. **bidding.strategy** `string` *required* `maximizeClicks`, `manualCpc`, or `maximizeConversions`. Any other answers `code: 101`. **euPoliticalAdvertising** `boolean` *required* Google requires this declaration on every campaign. While any campaign in an account is undeclared, Google rejects every change to that account's campaigns. **schedule** `object` Google schedules whole days in the ad account's time zone. `start` begins at 00:00:00 on its day and `end` stops at 23:59:59 on its day; a time you send only picks the day. `start` can be today but not an earlier day. For a one-day campaign, send the same date for `start` and `end`. An `end` on an earlier day than `start` answers `code: 101`. **targeting** `object` Google matches Search ads to people by the language of your ad text, so there is no language setting. Sending `targeting.languages` answers `code: 101`. **properties** **locations** `string[]` Google location ids, for example `2840` for the United States. Find ids in Google's [geo targets list](https://developers.google.com/google-ads/api/data/geotargets). Leave it out to target every location. **platformOptions.google** `object` *required* **properties** **finalUrl** `string` *required* The `http` or `https` page the ad opens. **headlines** `string[]` *required* 3 to 15 headlines, each up to 30 characters. **descriptions** `string[]` *required* 2 to 4 descriptions, each up to 90 characters. **path1** `string` First part of the display path shown after the domain, up to 15 characters. **path2** `string` Second part of the display path, up to 15 characters. Needs `path1`. **keywords** `object[]` *required* At least one keyword: `{ "text": string, "matchType": "exact" | "phrase" | "broad" }`. `text` is up to 80 characters and 10 words. **maxCpc** `number` The most to pay per click, in the account currency. Required with `manualCpc`. With `maximizeClicks` it caps each bid. Not accepted with `maximizeConversions`. ```bash cURL theme={"system"} curl \ -H "Authorization: Bearer API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: 6f1c2d4e-0b7a-4c55-9a3e-2f8d1b7c9e10" \ -X POST https://api.ayrshare.com/api/ads/google/campaigns \ -d '{ "accountId": "123-456-7890", "name": "Fall sale", "type": "search", "budget": { "amount": 25, "type": "daily" }, "bidding": { "strategy": "maximizeClicks" }, "targeting": { "locations": ["2840"] }, "euPoliticalAdvertising": false, "platformOptions": { "google": { "finalUrl": "https://example.com/fall-sale", "headlines": ["Fall sale", "Shoes 30% off", "Free returns"], "descriptions": ["Every shoe 30% off this week.", "Free returns on every order."], "path1": "shoes", "keywords": [ { "text": "running shoes", "matchType": "phrase" }, { "text": "trail running shoes", "matchType": "exact" } ] } } }' ``` ```javascript JavaScript theme={"system"} const API_KEY = "API_KEY"; fetch("https://api.ayrshare.com/api/ads/google/campaigns", { method: "POST", headers: { "Authorization": `Bearer ${API_KEY}`, "Content-Type": "application/json", "Idempotency-Key": "6f1c2d4e-0b7a-4c55-9a3e-2f8d1b7c9e10" }, body: JSON.stringify({ "accountId": "123-456-7890", "name": "Fall sale", "type": "search", "budget": { "amount": 25, "type": "daily" }, "bidding": { "strategy": "maximizeClicks" }, "targeting": { "locations": ["2840"] }, "euPoliticalAdvertising": false, "platformOptions": { "google": { "finalUrl": "https://example.com/fall-sale", "headlines": ["Fall sale", "Shoes 30% off", "Free returns"], "descriptions": ["Every shoe 30% off this week.", "Free returns on every order."], "path1": "shoes", "keywords": [ { "text": "running shoes", "matchType": "phrase" }, { "text": "trail running shoes", "matchType": "exact" } ] } } }) }) .then((res) => res.json()) .then((json) => console.log(json)) .catch(console.error); ``` ```python Python theme={"system"} import requests headers = { "Authorization": "Bearer API_KEY", "Idempotency-Key": "6f1c2d4e-0b7a-4c55-9a3e-2f8d1b7c9e10", } payload = { "accountId": "123-456-7890", "name": "Fall sale", "type": "search", "budget": { "amount": 25, "type": "daily" }, "bidding": { "strategy": "maximizeClicks" }, "targeting": { "locations": ["2840"] }, "euPoliticalAdvertising": False, "platformOptions": { "google": { "finalUrl": "https://example.com/fall-sale", "headlines": ["Fall sale", "Shoes 30% off", "Free returns"], "descriptions": ["Every shoe 30% off this week.", "Free returns on every order."], "path1": "shoes", "keywords": [ { "text": "running shoes", "matchType": "phrase" }, { "text": "trail running shoes", "matchType": "exact" } ] } } } r = requests.post("https://api.ayrshare.com/api/ads/google/campaigns", headers=headers, json=payload) print(r.json()) ``` ```php PHP theme={"system"} "https://api.ayrshare.com/api/ads/google/campaigns", CURLOPT_RETURNTRANSFER => true, CURLOPT_CUSTOMREQUEST => "POST", CURLOPT_HTTPHEADER => [ "Authorization: Bearer API_KEY", "Content-Type: application/json", "Idempotency-Key: 6f1c2d4e-0b7a-4c55-9a3e-2f8d1b7c9e10" ], CURLOPT_POSTFIELDS => $payload ]); $response = curl_exec($curl); $err = curl_error($curl); curl_close($curl); if ($err) { echo "cURL Error: " . $err; } else { echo $response; } ?> ``` ```csharp C# theme={"system"} using System; using System.Net.Http; using System.Text; using System.Threading.Tasks; class Program { static async Task Main(string[] args) { var client = new HttpClient(); client.DefaultRequestHeaders.Add("Authorization", "Bearer API_KEY"); client.DefaultRequestHeaders.Add("Idempotency-Key", "6f1c2d4e-0b7a-4c55-9a3e-2f8d1b7c9e10"); var payload = """ { "accountId": "123-456-7890", "name": "Fall sale", "type": "search", "budget": { "amount": 25, "type": "daily" }, "bidding": { "strategy": "maximizeClicks" }, "targeting": { "locations": ["2840"] }, "euPoliticalAdvertising": false, "platformOptions": { "google": { "finalUrl": "https://example.com/fall-sale", "headlines": ["Fall sale", "Shoes 30% off", "Free returns"], "descriptions": ["Every shoe 30% off this week.", "Free returns on every order."], "path1": "shoes", "keywords": [ { "text": "running shoes", "matchType": "phrase" }, { "text": "trail running shoes", "matchType": "exact" } ] } } } """; var response = await client.PostAsync( "https://api.ayrshare.com/api/ads/google/campaigns", new StringContent(payload, Encoding.UTF8, "application/json")); var content = await response.Content.ReadAsStringAsync(); Console.WriteLine(content); } } ``` ```go Go theme={"system"} package main import ( "fmt" "io" "net/http" "strings" ) func main() { payload := `{ "accountId": "123-456-7890", "name": "Fall sale", "type": "search", "budget": { "amount": 25, "type": "daily" }, "bidding": { "strategy": "maximizeClicks" }, "targeting": { "locations": ["2840"] }, "euPoliticalAdvertising": false, "platformOptions": { "google": { "finalUrl": "https://example.com/fall-sale", "headlines": ["Fall sale", "Shoes 30% off", "Free returns"], "descriptions": ["Every shoe 30% off this week.", "Free returns on every order."], "path1": "shoes", "keywords": [ { "text": "running shoes", "matchType": "phrase" }, { "text": "trail running shoes", "matchType": "exact" } ] } } }` req, _ := http.NewRequest("POST", "https://api.ayrshare.com/api/ads/google/campaigns", strings.NewReader(payload)) req.Header.Add("Authorization", "Bearer API_KEY") req.Header.Add("Content-Type", "application/json") req.Header.Add("Idempotency-Key", "6f1c2d4e-0b7a-4c55-9a3e-2f8d1b7c9e10") resp, err := http.DefaultClient.Do(req) if err != nil { fmt.Println("Error:", err) return } defer resp.Body.Close() body, _ := io.ReadAll(resp.Body) fmt.Println(string(body)) } ``` ```java Java theme={"system"} import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; public class CreateCampaign { public static void main(String[] args) throws Exception { String payload = """ { "accountId": "123-456-7890", "name": "Fall sale", "type": "search", "budget": { "amount": 25, "type": "daily" }, "bidding": { "strategy": "maximizeClicks" }, "targeting": { "locations": ["2840"] }, "euPoliticalAdvertising": false, "platformOptions": { "google": { "finalUrl": "https://example.com/fall-sale", "headlines": ["Fall sale", "Shoes 30% off", "Free returns"], "descriptions": ["Every shoe 30% off this week.", "Free returns on every order."], "path1": "shoes", "keywords": [ { "text": "running shoes", "matchType": "phrase" }, { "text": "trail running shoes", "matchType": "exact" } ] } } } """; HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("https://api.ayrshare.com/api/ads/google/campaigns")) .header("Authorization", "Bearer API_KEY") .header("Content-Type", "application/json") .header("Idempotency-Key", "6f1c2d4e-0b7a-4c55-9a3e-2f8d1b7c9e10") .POST(HttpRequest.BodyPublishers.ofString(payload)) .build(); HttpResponse response = HttpClient.newHttpClient() .send(request, HttpResponse.BodyHandlers.ofString()); System.out.println(response.body()); } } ``` ```ruby Ruby theme={"system"} require 'net/http' require 'uri' uri = URI.parse('https://api.ayrshare.com/api/ads/google/campaigns') request = Net::HTTP::Post.new(uri) request['Authorization'] = 'Bearer API_KEY' request['Content-Type'] = 'application/json' request['Idempotency-Key'] = '6f1c2d4e-0b7a-4c55-9a3e-2f8d1b7c9e10' request.body = <<~JSON { "accountId": "123-456-7890", "name": "Fall sale", "type": "search", "budget": { "amount": 25, "type": "daily" }, "bidding": { "strategy": "maximizeClicks" }, "targeting": { "locations": ["2840"] }, "euPoliticalAdvertising": false, "platformOptions": { "google": { "finalUrl": "https://example.com/fall-sale", "headlines": ["Fall sale", "Shoes 30% off", "Free returns"], "descriptions": ["Every shoe 30% off this week.", "Free returns on every order."], "path1": "shoes", "keywords": [ { "text": "running shoes", "matchType": "phrase" }, { "text": "trail running shoes", "matchType": "exact" } ] } } } JSON response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http| http.request(request) end puts response.body ``` ```json 200: Campaign Created theme={"system"} { "status": "success", "platform": "google", "campaignId": "21534567890", "adGroupId": "16812345678", "adId": "71234567890", "campaignStatus": "paused" } ``` ```json 400: Too Few Headlines theme={"system"} { "action": "request", "status": "error", "code": 101, "message": "Missing or incorrect parameters. Please verify with the docs. https://www.ayrshare.com/docs/apis", "details": "platformOptions.google.headlines: Supply at least 3 headlines." } ``` ## Errors | Code | When | | - | - | | `101` | A field is missing or invalid. `details` names it. | | `205` | Google refused the request: for example, the account already has a campaign with this name, or the ad text breaks Google's advertising policies. `details` says what to change. | | `400` | The Google user you linked cannot access this ad account, Google Ads access was revoked, the campaign is not yours, or a change targets a campaign removed in Google Ads. | | `370` | Google could not complete the request, or Google has not yet approved Ayrshare's access. `details` includes Google's request id when there is one. A create that answers `370` because Google's answer was lost may still have created the campaign. Google refuses a second campaign with the same name in one account, so retrying with the same `name` answers `205` instead of creating a duplicate. | # Update Campaign Source: https://www.ayrshare.com/docs/apis/ads/google/update-campaign PUT /ads/google/campaigns/{id} Start or pause a Google Ads campaign, rename it, or change its daily budget or end date. Start or pause a Google Ads campaign you created through Ayrshare, rename it, or change its daily budget or end date.
  • Send at least one field. All changes in one request apply together or not at all.
  • You cannot change `type`, `accountId` or `euPoliticalAdvertising` after you create the campaign.
  • No `Idempotency-Key` is needed: sending the same change again sets the same values.
  • The response returns the campaign's `campaignStatus`. Use [Get Campaign](/docs/apis/ads/google/get-campaign) to read the other fields.
## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` **Profile-Key** `string` [Profile Key](/docs/apis/overview#profile-key-format) of a User Profile. Format: `Profile-Key: PROFILE_KEY` ## Path Parameters **id** `string` *required* The `campaignId` returned when you created the campaign. ## Body Parameters **status** `string` `active` or `paused`. A request that only sends `status: "paused"` is not blocked when Ayrshare has paused ads on Google. **name** `string` The new campaign name, 1 to 255 characters, with no line breaks. **budget** `object` `{ "amount": number }`: the new amount to spend per day, in the ad account's currency, with at most two decimal places. A campaign whose budget you shared with other campaigns in Google Ads answers `code: 101`: change the shared budget in Google Ads. **schedule** `object` `{ "end": string }`: the new end date, as an ISO date (`2026-11-30`) or date-time. Google schedules whole days in the ad account's time zone, so the campaign stops at 23:59:59 on that day. It can be the same day as the start, but not an earlier one. ## Errors | Code | When | | - | - | | `101` | No field was sent, a field is invalid, or the budget is shared with other campaigns. `details` says which. | | `205` | Google refused the change. `details` says what to change. | | `400` | The campaign is not yours, does not exist, or was removed in Google Ads. Also when the Google user you linked cannot access the ad account, or Google Ads access was revoked. | | `370` | Google could not complete the request. Try again. | ```bash cURL theme={"system"} curl \ -H "Authorization: Bearer API_KEY" \ -H "Content-Type: application/json" \ -X PUT https://api.ayrshare.com/api/ads/google/campaigns/21534567890 \ -d '{"status": "active", "budget": { "amount": 30 }}' ``` ```javascript JavaScript theme={"system"} const API_KEY = "API_KEY"; fetch("https://api.ayrshare.com/api/ads/google/campaigns/21534567890", { method: "PUT", headers: { "Authorization": `Bearer ${API_KEY}`, "Content-Type": "application/json" }, body: JSON.stringify({"status": "active", "budget": { "amount": 30 }}) }) .then((res) => res.json()) .then((json) => console.log(json)) .catch(console.error); ``` ```python Python theme={"system"} import requests headers = { "Authorization": "Bearer API_KEY", } payload = {"status": "active", "budget": { "amount": 30 }} r = requests.put("https://api.ayrshare.com/api/ads/google/campaigns/21534567890", headers=headers, json=payload) print(r.json()) ``` ```php PHP theme={"system"} "https://api.ayrshare.com/api/ads/google/campaigns/21534567890", CURLOPT_RETURNTRANSFER => true, CURLOPT_CUSTOMREQUEST => "PUT", CURLOPT_HTTPHEADER => [ "Authorization: Bearer API_KEY", "Content-Type: application/json" ], CURLOPT_POSTFIELDS => $payload ]); $response = curl_exec($curl); $err = curl_error($curl); curl_close($curl); if ($err) { echo "cURL Error: " . $err; } else { echo $response; } ?> ``` ```csharp C# theme={"system"} using System; using System.Net.Http; using System.Text; using System.Threading.Tasks; class Program { static async Task Main(string[] args) { var client = new HttpClient(); client.DefaultRequestHeaders.Add("Authorization", "Bearer API_KEY"); var payload = """ {"status": "active", "budget": { "amount": 30 }} """; var response = await client.PutAsync( "https://api.ayrshare.com/api/ads/google/campaigns/21534567890", new StringContent(payload, Encoding.UTF8, "application/json")); var content = await response.Content.ReadAsStringAsync(); Console.WriteLine(content); } } ``` ```go Go theme={"system"} package main import ( "fmt" "io" "net/http" "strings" ) func main() { payload := `{"status": "active", "budget": { "amount": 30 }}` req, _ := http.NewRequest("PUT", "https://api.ayrshare.com/api/ads/google/campaigns/21534567890", strings.NewReader(payload)) req.Header.Add("Authorization", "Bearer API_KEY") req.Header.Add("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) if err != nil { fmt.Println("Error:", err) return } defer resp.Body.Close() body, _ := io.ReadAll(resp.Body) fmt.Println(string(body)) } ``` ```java Java theme={"system"} import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; public class UpdateCampaign { public static void main(String[] args) throws Exception { String payload = """ {"status": "active", "budget": { "amount": 30 }} """; HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("https://api.ayrshare.com/api/ads/google/campaigns/21534567890")) .header("Authorization", "Bearer API_KEY") .header("Content-Type", "application/json") .PUT(HttpRequest.BodyPublishers.ofString(payload)) .build(); HttpResponse response = HttpClient.newHttpClient() .send(request, HttpResponse.BodyHandlers.ofString()); System.out.println(response.body()); } } ``` ```ruby Ruby theme={"system"} require 'net/http' require 'uri' uri = URI.parse('https://api.ayrshare.com/api/ads/google/campaigns/21534567890') request = Net::HTTP::Put.new(uri) request['Authorization'] = 'Bearer API_KEY' request['Content-Type'] = 'application/json' request.body = <<~JSON {"status": "active", "budget": { "amount": 30 }} JSON response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http| http.request(request) end puts response.body ``` ```json 200: Success theme={"system"} { "status": "success", "platform": "google", "campaignId": "21534567890", "campaignStatus": "active" } ``` ```json 400: Shared Budget theme={"system"} { "action": "request", "status": "error", "code": 101, "message": "Missing or incorrect parameters. Please verify with the docs. https://www.ayrshare.com/docs/apis", "details": "This campaign uses a budget shared with other campaigns in Google Ads. Change the shared budget in Google Ads." } ``` # Boost Post Source: https://www.ayrshare.com/docs/apis/ads/instagram/boost-post POST /ads/instagram/boost Boost an existing Instagram post into a paid ad with custom targeting, budget, scheduling, and ad goals, plus the payment method and settings Meta requires. Boost an existing Instagram post to create an ad. This endpoint allows you to convert your organic posts into paid advertisements with custom targeting, budget, and scheduling parameters.
  • Budget and bid amounts are in the ad account's currency. Use up to two decimal places, or whole numbers for the currencies Meta takes in whole units: CLP, COP, CRC, HUF, IDR, ISK, JPY, KRW, PYG, TWD and VND.
  • The ad must run for at least 30 hours to address the Instagram requirement.
  • You can use the [interests endpoint](/docs/apis/ads/instagram/get-ad-interests) to find interest IDs for targeting.
  • Instagram may take up to 24 hours to review and approve boosted posts.
  • If using `socialPostId` directly (instead of Ayrshare `postId`), ensure it's a valid Instagram post ID.
  • If a boost fails after Facebook has created its campaign (at the creative, ad set or ad step), we try to delete that campaign. If the delete also fails, the empty campaign stays in your ad account. You can find and delete it in Ads Manager.
> [!NOTE] > > Meta refuses the ad until both of these are in place: > > * **A payment method on the ad account.** Add one in Meta's [Billing & payments](https://business.facebook.com/billing_hub/payment_settings/?placement=ads_manager) and attach it to the ad account you send as `accountId`. A payment method saved only on the business portfolio is not enough. > * **Personalized ads turned on (EU, EEA and Switzerland).** If the owner of the Facebook or Instagram account linked to your Facebook Page chose to see less-personalized ads, or subscribed to use Meta without ads, Meta does not let that account or the Page run ads. The owner can switch back to personalized ads, or cancel the subscription, in Accounts Center → Ad preferences → Ad experience. [Learn more](https://www.facebook.com/business/help/1563729837497242). ### Ad Goals Each ad must have a goal. The goal determines how the ad will be optimized for display.
  • `engagement`: This goal seeks to increase engagement while ensuring that the ad reaches the maximum number of unique users. It balances visibility with engagement, showing the ad to as many different people as possible who may interact with it.
  • `interactions`: Designed to boost interactions, such as likes, comments, and shares, on the ad. Instagram prioritizes showing the ad to users most likely to engage with it.
  • `awareness_views`: Focuses on increasing brand awareness by maximizing the number of times the ad is displayed. It prioritizes showing the ad as many times as possible within the budget, regardless of unique reach.
  • `awareness_audience`: Aims to enhance brand awareness by maximizing the number of unique people who see the ad. It ensures that the ad reaches as many different users as possible and maximizes the unique audience size rather than showing it multiple times to the same audience.
> [!NOTE] > > Instagram ads require an Instagram Business account connected through Facebook, an associated Facebook Page, and the `ads_management` permission. Relink through Facebook to grant this permission. Accounts connected with Instagram Login cannot use these ads endpoints. The media ID and Instagram account ID are resolved from your linked account; do not send `instagram_user_id`. The `socialPostId` selector also accepts the Instagram-specific alias `igPostId`. `fbPostId` applies only to Facebook. Successful responses include `platform: "instagram"`, `socialPostId`, and `igPostId`. ## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` **Profile-Key** `string` [Profile Key](/docs/apis/overview#profile-key-format) of a User Profile. Format: `Profile-Key: PROFILE_KEY` ## Body Parameters **idempotencyKey** `string` A unique, non-empty key for this boost request. Retry the same payload with the same key to avoid creating a duplicate ad. Reusing the key with a different payload, or for a Facebook boost, returns HTTP 409. On any other platform, the same key starts a separate boost. A request still in progress also returns 409; an unavailable reservation returns 503 without creating an ad. **adSetBudgetSharingEnabled** `boolean` (default: false) Allow Meta to share budget between ad sets in the campaign. **accountId** `string` *required* The ID of the Instagram ad account to boost the post on. The account ID can be retrieved from the [ad accounts endpoint](/docs/apis/ads/instagram/get-ad-accounts). **adName** `string` *required* Name for your ad (appears in Instagram Ad Manager) with the format `{adName} - {postId or socialPostId} - {current date}`. If you send an `idempotencyKey`, the name also ends in ` - {request reference}`, a short reference that stays the same for that key. **bidAmount** `number` *required* Maximum bid amount in the ad account's currency, as a positive amount. **budget** `number` *required* Daily budget in the ad account's currency, as a positive amount. Meta may enforce minimums for your account and goal. **socialPostId** `string` The [Instagram social post ID](/docs/apis/overview#social-post-id) of the post to boost, which allows you to create an ad from a post created directly on Instagram. This is the ID of the [post on Instagram](/docs/apis/history/history-platform), not Ayrshare. Required if `postId` is not set. **goal** `string` (default: engagement) The goal of the ad. Values: `engagement`, `interactions`, `awareness_views`, and `awareness_audience`. See [ad goals details](/docs/apis/ads/instagram/boost-post#ad-goals) above for more information. **locations** `object` Target countries with `{"countries": ["US", "CA"]}` using [country codes](/docs/iso-codes/country). You can also supply `regions` and `cities` arrays; see [regions](/docs/apis/ads/instagram/get-ad-regions) and [cities](/docs/apis/ads/instagram/get-ad-cities). An object that names no country, region or city, such as `{}`, is refused with code `101`; leave `locations` out to target the US. **postId** `string` The [Ayrshare post ID](/docs/apis/overview#ayrshare-post-id) of the post to boost. Required if `socialPostId` is not set. **status** `string` (default: active) The status of the ad. Values: `active` and `paused`. You can later change the status of the ad using the [update ad endpoint](/docs/apis/ads/instagram/put-ad-update). **tracking** `object` Track the ad using a Meta Pixel. **child attributes** **pixelId** `number` *required* The ID of the Meta Pixel to track the ad. ```json theme={"system"} { "pixelId": 1234567890 } ``` **urlTags** `array` Add UTM tags to the ad URL. ```json theme={"system"} { "urlTags": ["utm_source=ayrshare", "utm_medium=social", "utm_campaign=ayrshare-social"] } ``` For example if the linking URL is `https://www.mysite.com/my-post` and the URL tags added are: `utm_source=ayrshare`, `utm_medium=social`, and `utm_campaign=ayrshare-social`. The ad URL will be: `https://www.mysite.com/my-post?utm_source=ayrshare&utm_medium=social&utm_campaign=ayrshare-social`. **specialAdCategories** `array` Meta requires advertisers in [special ad categories](https://www.facebook.com/business/help/298000447747885?helpref=faq_content) to self-identify their campaign category. If your business is in one of these categories, you must select the appropriate category when boosting your post. The following values are supported:
  • `housing`: Ads that promote or directly link to a housing opportunity or related service, including but not limited to listings for the sale or rental of a home or apartment, homeowners insurance, mortgage insurance, mortgage loans, housing repairs and home equity or appraisal services.
  • `financial_product_services`: Ads that promote or directly link to a financial products and services offer, including credit.
  • `employment`: Ads that promote or directly link to an employment opportunity, including but not limited to part- or full-time jobs, internships or professional certification programs. Related ads that fall within this category include promotions for job boards or fairs, aggregation services or ads detailing perks a company may provide, regardless of a specific job offer.
  • `issues_elections_politics`: Ads made by, on behalf of, or about a candidate for public office, a political figure, a political party or advocating for the outcome of an election to public office. This also includes ads about any election, referendum or ballot initiative, including "Go out and vote" election campaigns. Ads regulated as political advertising or about social issues in any place where the ad is being placed. If selecting issues, elections, or politics, you must select the country in which you want to run these ads. You are required to be authorized to run ads about social issues, elections, or politics in the specified country.
Meta also needs the countries a special category ad runs in. Ayrshare takes them from `locations`: the countries you list, plus the country of each region and city. So a `housing` boost that targets a city in the UK is set up for the UK, with nothing extra in your request. Without `locations` the ad runs in the US, and the campaign is set up for the US. Meta also sets a minimum radius around each city for these categories. A UK city with a 17 kilometer radius was rejected with `#2909039`, which asks for at least 25 kilometers (15 miles), so use 25 kilometers or more. > [!WARNING] > > Meta requires advertisers to identify their campaign category correctly. > Meta uses human reviewers and machine-learning to identify these kinds of ads. > If you send us an incorrect `specialAdCategories`, there is a risk your ads will be paused until the campaign is adjusted. **dsaBeneficiary** `string` The person or organization benefiting from the ad. Set together with `dsaPayor`. Use the [DSA recommendations endpoint](/docs/apis/ads/instagram/get-dsa-recommendations) for suggestions. **dsaPayor** `string` The person or organization paying for the ad. Set together with `dsaBeneficiary`. **endDate** `string` End date and time in ISO 8601 format (must be at least 30 hours after start), for example `2025-03-01T00:00:00Z`. If not set, the ad will run indefinitely and have end date of `ongoing`. **excludedLocations** `object` Exclude locations using the same `countries`, `regions`, and `cities` structure as `locations`. **gender** `string` (default: all) The gender of the audience. Values: `all`, `male`, `female`. **interests** `array` The target interests of the ad as an array of Instagram [interest ids](/docs/apis/ads/instagram/get-ad-interests). **maxAge** `number` (default: 65) Maximum age for targeting the ad (default: 65). **minAge** `number` (default: 18) Minimum age for targeting the ad (default: 18). **startDate** `string` Start date and time in ISO 8601 format, for example `2025-03-01T00:00:00Z`. If not set, the ad will start immediately. Replace the example IDs with your own. Set `startDate` and `endDate` to your intended schedule; the example dates are illustrative. ## Lifetime Budgets The body above sets a daily budget on the ad set. For a lifetime budget, send the nested form of the boost body instead. In the nested form, `accountId`, `postId` (or `socialPostId` in place of `igPostId`), `goal`, `status` and `callToAction` stay at the top level. Three objects replace the flat budget, date and targeting fields: `budget`, `schedule` (`start`, `end`) and `targeting` (`locations`, `excludedLocations`, `ageMin`, `ageMax`, `genders`, `interests`). The remaining fields, `adName`, `bidAmount`, `specialAdCategories`, `adSetBudgetSharingEnabled`, `link`, `tracking`, `urlTags`, `dsaBeneficiary` and `dsaPayor`, move under `platformOptions.instagram`. ```json theme={"system"} { "accountId": "123456789", "postId": "Ut1fWU6XkqkMayHGnJZ", "goal": "engagement", "budget": { "amount": 50, "type": "lifetime" }, "schedule": { "start": "2026-11-01T00:00:00Z", "end": "2026-11-08T00:00:00Z" }, "targeting": { "ageMin": 25, "ageMax": 45, "locations": { "countries": ["US"] } }, "platformOptions": { "instagram": { "adName": "November boost", "bidAmount": 2 } } } ``` * `budget.type`: `daily` (the default) or `lifetime`. A lifetime budget is spent between the start and end dates, so it needs `schedule.end`. Without one the request is rejected with a `400` and `code: 101`. Meta's ad set budget sharing works with daily budgets only, so a lifetime budget with `adSetBudgetSharingEnabled: true` is also rejected with `code: 101`. The response adds `budgetType: "lifetime"` for a lifetime budget. A daily budget does not return `budgetType`, so treat a missing field as `daily`. ## Campaign Budgets To hold the budget on the campaign instead of the ad set, send `budget.level: "campaign"` in the nested form shown above. The ad set then has no budget of its own. It works with daily and lifetime budgets. ```json theme={"system"} { "accountId": "123456789", "postId": "Ut1fWU6XkqkMayHGnJZ", "goal": "engagement", "budget": { "amount": 50, "type": "lifetime", "level": "campaign" }, "schedule": { "start": "2026-11-01T00:00:00Z", "end": "2026-11-08T00:00:00Z" }, "platformOptions": { "instagram": { "adName": "November boost", "bidAmount": 2 } } } ``` * `budget.level`: `adSet` (the default) or `campaign`. A campaign budget cannot be combined with `adSetBudgetSharingEnabled`. Sending both is rejected with a `400` and `code: 101`. * The response adds `budgetLevel: "campaign"` for a campaign budget. An ad set budget does not return `budgetLevel`, so treat a missing field as `adSet`. * In [History](/docs/apis/ads/instagram/get-ad-history), `budgetDaily` shows the campaign's daily budget for an ad with a daily campaign budget. If several ads share one campaign budget, each shows the whole campaign budget, not its share. History does not show lifetime budgets, on the campaign or the ad set. Once a lifetime campaign budget is spent, every ad in that campaign shows `deliveryStatus: "COMPLETED"` after the next nightly update, including an ad that is scheduled or in review. A rejected ad stays `REJECTED`. ## Boost into an Existing Campaign or Ad Set By default every boost creates its own campaign, ad set and ad. To add the boost to a campaign or ad set you already run in Meta, send the nested form with `attachTo`. * `attachTo.campaignId`: the boost creates a new ad set and ad inside that campaign. Send `budget`, `targeting` and `bidAmount` as usual: they apply to the new ad set. * `attachTo.adSetId`: the boost creates only the ad, inside that ad set. Also send `attachTo.campaignId`, the campaign the ad set belongs to. Leave out `budget`, `targeting` and `platformOptions.instagram.bidAmount`: the ad set keeps its own. Sending any of them returns a `400` with `code: 101`. ```json theme={"system"} { "accountId": "123456789", "postId": "Ut1fWU6XkqkMayHGnJZ", "goal": "engagement", "attachTo": { "campaignId": "120210000000000001", "adSetId": "120210000000000002" }, "platformOptions": { "instagram": { "adName": "November boost" } } } ``` * The campaign or ad set must be in the ad account named by `accountId`, and must not belong to another user's boosts. Otherwise the request returns a `400`. An Instagram boost also cannot attach to a campaign a Facebook boost created, and the reverse. * `attachTo` cannot be combined with `budget.level: "campaign"`, `specialAdCategories` or `adSetBudgetSharingEnabled`, which are set on a new campaign. Sending them returns `code: 101`. * Pausing or activating the boost changes only what the boost created: the ad, and the ad set when the boost created it. The campaign or ad set you named in `attachTo` keeps its own status. The ad runs only while the campaign and ad set above it are active. This also applies when Ayrshare pauses your ads, for example when a profile is deleted. * If the boost fails, the campaign or ad set named in `attachTo` is never deleted. * If the ad set you attach to sits in a campaign that holds the budget (a campaign budget), [History](/docs/apis/ads/instagram/get-ad-history) reports the boost like any ad with a campaign budget: `budgetDaily` shows the whole campaign budget, not the boost's share, and once a lifetime campaign budget is spent, every ad Ayrshare tracks in that campaign shows `deliveryStatus: "COMPLETED"` after the next nightly update, including an ad that is scheduled or in review. A rejected ad stays `REJECTED`. ## Open a Conversation Instead of a Link A boost's button can open a chat with your business instead of a web page. Send the nested form with `callToAction.destination`, and leave out `callToAction.link`. | `destination` | Opens | `callToAction.type` | | - | - | - | | `messenger` | Messenger | `MESSAGE_PAGE` | | `whatsapp` | WhatsApp | `WHATSAPP_MESSAGE` | | `instagramDm` | Instagram Direct | `INSTAGRAM_MESSAGE` | ```json theme={"system"} { "accountId": "123456789", "postId": "Ut1fWU6XkqkMayHGnJZ", "goal": "engagement", "budget": { "amount": 20 }, "callToAction": { "type": "WHATSAPP_MESSAGE", "destination": "whatsapp" }, "platformOptions": { "instagram": { "adName": "Chat with us", "bidAmount": 2 } } } ``` * Use the `engagement` or `interactions` [ad goal](/docs/apis/ads/instagram/boost-post#ad-goals). The ad is then optimized for conversations: Meta shows it to people likely to start a chat. Other goals return a `400` with `code: 101`. * `callToAction.type` must match the destination, as in the table. Any casing is accepted. * `callToAction.link` and `callToAction.linkTitle` cannot be sent with `destination`, because the button opens a chat, not a page. Without `destination`, `link` is still required. * People message the account linked to your profile. For `whatsapp`, its Facebook Page must have a WhatsApp Business number connected in Meta. * `destination` cannot be combined with `attachTo.adSetId`: the existing ad set keeps the destination it already has. With `attachTo.campaignId`, the campaign must have Meta's `OUTCOME_ENGAGEMENT` objective; otherwise the request returns a `400` with `code: 101`. * The response, and [Get Boosted Ads](/docs/apis/ads/instagram/get-boosted-ads), return `callToAction` with its `destination`. Meta stores the same settings for both goals, so a chat button boosted with `interactions` can be listed with the `engagement` goal when the ad is read back from Meta. ```bash cURL theme={"system"} curl -X POST https://api.ayrshare.com/api/ads/instagram/boost \ -H "Authorization: Bearer API_KEY" \ -H "Content-Type: application/json" \ -d '{ "postId": "1234567890", "accountId": "1234567890", "adName": "My Ad", "status": "active", "goal": "engagement", "minAge": 18, "maxAge": 65, "locations": {"countries": ["US"]}, "budget": 100, "bidAmount": 1, "startDate": "2025-03-01T00:00:00Z", "endDate": "2025-03-07T23:59:59Z", "interests": [1234567890, 1234567891] }' ``` ```javascript JavaScript theme={"system"} const API_KEY = "API_KEY"; fetch("https://api.ayrshare.com/api/ads/instagram/boost", { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${API_KEY}` }, body: JSON.stringify({ postId: "1234567890", accountId: "1234567890", adName: "My Ad", status: "active", goal: "engagement", minAge: 18, maxAge: 65, locations: { countries: ["US"] }, budget: 100, bidAmount: 1, startDate: "2025-03-01T00:00:00Z", endDate: "2025-03-07T23:59:59Z", interests: [1234567890, 1234567891] }) }) .then((res) => res.json()) .then((json) => console.log(json)) .catch(console.error); ``` ```python Python theme={"system"} import requests API_KEY = "API_KEY" url = "https://api.ayrshare.com/api/ads/instagram/boost" payload = { "postId": "1234567890", "accountId": "1234567890", "adName": "My Ad", "status": "active", "goal": "engagement", "minAge": 18, "maxAge": 65, "locations": {"countries": ["US"]}, "budget": 100, "bidAmount": 1, "startDate": "2025-03-01T00:00:00Z", "endDate": "2025-03-07T23:59:59Z", "interests": [1234567890, 1234567891] } response = requests.post(url, headers={"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"}, json=payload) print(response.json()) ``` ```php PHP theme={"system"} "1234567890", "accountId" => "1234567890", "adName" => "My Ad", "status" => "active", "goal" => "engagement", "minAge" => 18, "maxAge" => 65, "locations" => ["countries" => ["US"]], "budget" => 100, "bidAmount" => 1, "startDate" => "2025-03-01T00:00:00Z", "endDate" => "2025-03-07T23:59:59Z", "interests" => [1234567890, 1234567891] ]; $ch = curl_init("https://api.ayrshare.com/api/ads/instagram/boost"); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload)); curl_setopt($ch, CURLOPT_HTTPHEADER, [ "Content-Type: application/json", "Authorization: Bearer " . $API_KEY ]); $response = curl_exec($ch); $result = json_decode($response, true); print_r($result); curl_close($ch); ``` ```csharp C# theme={"system"} using System; using System.Net.Http; using System.Net.Http.Headers; using System.Text; using System.Text.Json; using System.Threading.Tasks; class Program { static async Task Main() { string API_KEY = "API_KEY"; var payload = new { postId = "1234567890", accountId = "1234567890", adName = "My Ad", status = "active", goal = "engagement", minAge = 18, maxAge = 65, locations = new { countries = new[] { "US" } }, budget = 100, bidAmount = 1, startDate = "2025-03-01T00:00:00Z", endDate = "2025-03-07T23:59:59Z", interests = new[] { 1234567890, 1234567891 } }; using var client = new HttpClient(); client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", API_KEY); var content = new StringContent( JsonSerializer.Serialize(payload), Encoding.UTF8, "application/json" ); var response = await client.PostAsync("https://api.ayrshare.com/api/ads/instagram/boost", content); var responseBody = await response.Content.ReadAsStringAsync(); Console.WriteLine(responseBody); } } ``` ```go Go theme={"system"} package main import ( "bytes" "encoding/json" "fmt" "io/ioutil" "net/http" ) func main() { apiKey := "API_KEY" payload := map[string]interface{}{ "postId": "1234567890", "accountId": "1234567890", "adName": "My Ad", "status": "active", "goal": "engagement", "minAge": 18, "maxAge": 65, "locations": map[string]interface{}{"countries": []string{"US"}}, "budget": 100, "bidAmount": 1, "startDate": "2025-03-01T00:00:00Z", "endDate": "2025-03-07T23:59:59Z", "interests": []int{1234567890, 1234567891}, } jsonData, err := json.Marshal(payload) if err != nil { fmt.Println("Error marshaling JSON:", err) return } req, err := http.NewRequest("POST", "https://api.ayrshare.com/api/ads/instagram/boost", bytes.NewBuffer(jsonData)) if err != nil { fmt.Println("Error creating request:", err) return } req.Header.Set("Content-Type", "application/json") req.Header.Set("Authorization", "Bearer "+apiKey) client := &http.Client{} resp, err := client.Do(req) if err != nil { fmt.Println("Error sending request:", err) return } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { fmt.Println("Error reading response:", err) return } fmt.Println(string(body)) } ``` ```java Java theme={"system"} import java.io.IOException; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import com.fasterxml.jackson.databind.ObjectMapper; public class BoostPost { public static void main(String[] args) { String API_KEY = "API_KEY"; String url = "https://api.ayrshare.com/api/ads/instagram/boost"; Map payload = new HashMap<>(); payload.put("postId", "1234567890"); payload.put("accountId", "1234567890"); payload.put("adName", "My Ad"); payload.put("status", "active"); payload.put("goal", "engagement"); payload.put("minAge", 18); payload.put("maxAge", 65); payload.put("locations", Map.of("countries", Arrays.asList("US"))); payload.put("budget", 100); payload.put("bidAmount", 1); payload.put("startDate", "2025-03-01T00:00:00Z"); payload.put("endDate", "2025-03-07T23:59:59Z"); payload.put("interests", Arrays.asList(1234567890, 1234567891)); try { ObjectMapper objectMapper = new ObjectMapper(); String requestBody = objectMapper.writeValueAsString(payload); HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(url)) .header("Content-Type", "application/json") .header("Authorization", "Bearer " + API_KEY) .POST(HttpRequest.BodyPublishers.ofString(requestBody)) .build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); System.out.println(response.body()); } catch (IOException | InterruptedException e) { e.printStackTrace(); } } } ``` ```ruby Ruby theme={"system"} require 'net/http' require 'uri' require 'json' API_KEY = "API_KEY" uri = URI.parse("https://api.ayrshare.com/api/ads/instagram/boost") payload = { postId: "1234567890", accountId: "1234567890", adName: "My Ad", status: "active", goal: "engagement", minAge: 18, maxAge: 65, locations: { countries: ["US"] }, budget: 100, bidAmount: 1, startDate: "2025-03-01T00:00:00Z", endDate: "2025-03-07T23:59:59Z", interests: [1234567890, 1234567891] } http = Net::HTTP.new(uri.host, uri.port) http.use_ssl = true request = Net::HTTP::Post.new(uri.request_uri) request["Content-Type"] = "application/json" request["Authorization"] = "Bearer #{API_KEY}" request.body = payload.to_json response = http.request(request) puts response.body ``` ```json 200: Post Boosted theme={"system"} { "status": "success", "adId": "120217670757750410", "adName": "API Post - DE6gpw8kxlonHy2b7Lo - 2025-03-26T23:42:43", "adStatus": "active", "bidAmount": 10, "budget": 100, "endDate": "2026-03-28T22:30:00Z", "goal": { "title": "Get More Engagement", "description": "This goal seeks to increase engagement...", "type": "engagement" }, "interests": [ "6003195554098" ], "locations": {"countries": ["US"]}, "maxAge": 65, "minAge": 18, "postId": "DE6gpw8kxlonHy2b7Lo", "socialPostId": "1234567890", "igPostId": "1234567890", "platform": "instagram", "startDate": "2026-03-26T22:30:00Z" } ``` ```json 400: Invalid Account ID theme={"system"} { "action": "boost post", "status": "error", "code": 369, "message": "Unable to boost post. Please try again or contact us if the issue persists.", "details": "The accountId likely does not exist. Please check the accountId and try again." } ``` ```json 400: Missing Required Parameters theme={"system"} { "action": "request", "status": "error", "code": 101, "message": "Missing or incorrect parameters. Please verify with the docs. https://www.ayrshare.com/docs/apis", "details": "Missing required fields: accountId" } ``` # Accounts Source: https://www.ayrshare.com/docs/apis/ads/instagram/get-ad-accounts GET /ads/instagram/accounts List Meta ad accounts for Instagram ads on the authenticated profile with status and metrics. Needs an Instagram Business account linked through Facebook. Retrieve available Instagram ad accounts associated with the authenticated profile. This endpoint allows you to access basic account information as well as key metrics for the ad accounts.
  • The `metrics` object contains aggregate performance data across all ads in the account
  • Results are cached for 10 minutes to optimize performance and reduce API calls to Instagram
  • Account status in the JSON response may be one of: `active`, `disabled`, `unsettled`, `pending_risk_review`, `pending_settlement`, `in_grace_period`, `pending_closure`, `closed`, `any_active`, or `any_closed`
> [!NOTE] > > These endpoints require an Instagram Business account connected through Facebook with ads management permission. Relink through Facebook if that permission is missing. Instagram Login is not supported. Meta ad accounts can contain ads for both Facebook and Instagram; the account list is not an Instagram-only list. Submitted ads and history are scoped to Instagram. Use `facebook` or `instagram` in the ads URL. Other platform values return HTTP 400 with `code: 101`. ## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` **Profile-Key** `string` [Profile Key](/docs/apis/overview#profile-key-format) of a User Profile. Format: `Profile-Key: PROFILE_KEY` ## Query Parameters **limit** `number` (default: 100) Limit the number of ad accounts returned. ```bash cURL theme={"system"} curl \ -H "Authorization: Bearer API_KEY" \ -X GET https://api.ayrshare.com/api/ads/instagram/accounts ``` ```javascript JavaScript theme={"system"} const API_KEY = "API_KEY"; fetch("https://api.ayrshare.com/api/ads/instagram/accounts", { method: "GET", headers: { "Authorization": `Bearer ${API_KEY}` } }) .then((res) => res.json()) .then((json) => console.log(json)) .catch(console.error); ``` ```python Python theme={"system"} import requests headers = {'Authorization': 'Bearer API_KEY'} r = requests.get('https://api.ayrshare.com/api/ads/instagram/accounts', headers=headers) print(r.json()) ``` ```php PHP theme={"system"} "https://api.ayrshare.com/api/ads/instagram/accounts", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer API_KEY" ] ]); $response = curl_exec($curl); curl_close($curl); echo $response; ?> ``` ```csharp C# theme={"system"} using System; using System.Net.Http; using System.Threading.Tasks; class Program { static async Task Main(string[] args) { string apiKey = "API_KEY"; using (HttpClient client = new HttpClient()) { client.DefaultRequestHeaders.Add("Authorization", $"Bearer {apiKey}"); HttpResponseMessage response = await client.GetAsync("https://api.ayrshare.com/api/ads/instagram/accounts"); string content = await response.Content.ReadAsStringAsync(); Console.WriteLine(content); } } } ``` ```go Go theme={"system"} package main import ( "fmt" "io/ioutil" "net/http" ) func main() { apiKey := "API_KEY" req, _ := http.NewRequest("GET", "https://api.ayrshare.com/api/ads/instagram/accounts", nil) req.Header.Add("Authorization", "Bearer " + apiKey) client := &http.Client{} resp, err := client.Do(req) if err != nil { fmt.Println(err) return } defer resp.Body.Close() body, _ := ioutil.ReadAll(resp.Body) fmt.Println(string(body)) } ``` ```java Java theme={"system"} import java.io.BufferedReader; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URL; public class GetAdAccounts { public static void main(String[] args) { try { String apiKey = "API_KEY"; URL url = new URL("https://api.ayrshare.com/api/ads/instagram/accounts"); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("GET"); conn.setRequestProperty("Authorization", "Bearer " + apiKey); BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream())); String inputLine; StringBuffer response = new StringBuffer(); while ((inputLine = in.readLine()) != null) { response.append(inputLine); } in.close(); System.out.println(response.toString()); } catch (Exception e) { e.printStackTrace(); } } } ``` ```ruby Ruby theme={"system"} require 'net/http' require 'json' uri = URI('https://api.ayrshare.com/api/ads/instagram/accounts') req = Net::HTTP::Get.new(uri) req['Authorization'] = 'Bearer API_KEY' response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) } puts response.body ``` ```json 200: Success theme={"system"} { "status": "success", "adAccounts": [ { "accountId": "274948345", "ageInDays": 2870.86, "amountSpent": 191.33, "balance": 7.84, "budgetRemaining": 1, "business": { "name": "Fun Fun Fun", "city": "New York", "country": "US", "state": "NY", "street": "178 Columbus Ave", "zip": "10023" }, "created": "2012-01-05T03:11:31-0800", "currency": "USD", "disableReason": null, "fundingSource": { "id": "13157487252022", "type": "Credit Card" }, "hasNotificationsEnabled": true, "isPersonal": false, "isPrepayAccount": false, "metrics": { "spend": 191.33, "impressions": 24994, "reach": 20410, "clicks": 622, "ctr": 2.488597, "cpm": 7.655037, "cpp": 9.374326, "frequency": 1.224596, "uniqueClicks": 432, "uniqueCtr": 2.11661, "costPerUniqueClick": 0.442894, "inlineLinkClicks": 538, "costPerInlineLinkClick": 0.355632, "outboundClicks": 468, "costPerOutboundClick": 0.408825, "websiteCtr": [ { "action_type": "link_click", "value": "2.152517" } ], "accountCurrency": "USD", "accountName": "John Smith", "accountId": "274948321" }, "minDailyBudget": 1, "name": "John Smith", "ownerBusiness": { "name": "My Business" }, "spendCap": 0, "status": "active", "taxIdStatus": "NOT_REQUIRED", "timezoneId": 74, "timezoneName": "America/New_York", "timezoneOffsetHoursUtc": -5 } ], "count": 1, "lastUpdated": "2025-03-27T00:55:00.000Z", "nextUpdate": "2025-03-27T01:05:00.000Z" } ``` ```json 400: Ad accounts error theme={"system"} { "action": "get ad accounts", "status": "error", "code": 366, "message": "Error getting ad accounts. Please verify you have an active ad account at Meta. You also might not have granted permission to access your ad account. Please try unlinking and relinking the platform and try again. Contact us if the issue persists." } ``` # Campaigns Source: https://www.ayrshare.com/docs/apis/ads/instagram/get-ad-campaigns GET /ads/instagram/campaigns Retrieve campaigns for an Instagram ad account by accountId, with status, budgetRemaining, metrics, and scheduling details. Results cache for 10 minutes. Retrieve ad campaigns for a specific Instagram ad account. This endpoint provides detailed information about campaigns, including budget, status, performance metrics, and scheduling details.
  • Campaign status may be one of: "ACTIVE", "PAUSED", "DELETED", "ARCHIVED", "IN\_PROCESS", or "WITH\_ISSUES"
  • The `metrics` object contains performance data for the campaign
  • The `budgetRemaining` field represents the remaining budget in the account's currency
  • Results are cached for 10 minutes to optimize performance
## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` **Profile-Key** `string` [Profile Key](/docs/apis/overview#profile-key-format) of a User Profile. Format: `Profile-Key: PROFILE_KEY` ## Query Parameters **accountId** `string` *required* The ID of the ad account to retrieve campaigns for. The ad account id, with or without the `act_` prefix (`1234567890` or `act_1234567890`). **limit** `number` (default: 100) Limit the number of campaigns returned. ```bash cURL theme={"system"} curl \ -H "Authorization: Bearer API_KEY" \ -X GET https://api.ayrshare.com/api/ads/instagram/campaigns?accountId=1234567890 ``` ```javascript JavaScript theme={"system"} const API_KEY = "API_KEY"; fetch("https://api.ayrshare.com/api/ads/instagram/campaigns?accountId=1234567890", { method: "GET", headers: { "Authorization": `Bearer ${API_KEY}` } }) .then((res) => res.json()) .then((json) => console.log(json)) .catch(console.error); ``` ```python Python theme={"system"} import requests headers = {'Authorization': 'Bearer API_KEY'} r = requests.get('https://api.ayrshare.com/api/ads/instagram/campaigns?accountId=1234567890', headers=headers) print(r.json()) ``` ```php PHP theme={"system"} [ 'header' => "Authorization: Bearer " . $apiKey, 'method' => 'GET' ] ]; $context = stream_context_create($options); $response = file_get_contents($url, false, $context); $result = json_decode($response, true); print_r($result); ?> ``` ```csharp C# theme={"system"} using System; using System.Net.Http; using System.Threading.Tasks; class Program { static async Task Main() { string apiKey = "API_KEY"; string url = "https://api.ayrshare.com/api/ads/instagram/campaigns?accountId=1234567890"; using (HttpClient client = new HttpClient()) { client.DefaultRequestHeaders.Add("Authorization", $"Bearer {apiKey}"); HttpResponseMessage response = await client.GetAsync(url); string content = await response.Content.ReadAsStringAsync(); Console.WriteLine(content); } } } ``` ```go Go theme={"system"} package main import ( "fmt" "io/ioutil" "net/http" ) func main() { apiKey := "API_KEY" url := "https://api.ayrshare.com/api/ads/instagram/campaigns?accountId=1234567890" req, _ := http.NewRequest("GET", url, nil) req.Header.Add("Authorization", "Bearer " + apiKey) client := &http.Client{} resp, err := client.Do(req) if err != nil { fmt.Println(err) return } defer resp.Body.Close() body, _ := ioutil.ReadAll(resp.Body) fmt.Println(string(body)) } ``` ```java Java theme={"system"} import java.io.BufferedReader; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URL; public class Main { public static void main(String[] args) { try { String apiKey = "API_KEY"; URL url = new URL("https://api.ayrshare.com/api/ads/instagram/campaigns?accountId=1234567890"); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("GET"); conn.setRequestProperty("Authorization", "Bearer " + apiKey); BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream())); String inputLine; StringBuffer response = new StringBuffer(); while ((inputLine = in.readLine()) != null) { response.append(inputLine); } in.close(); System.out.println(response.toString()); } catch (Exception e) { e.printStackTrace(); } } } ``` ```ruby Ruby theme={"system"} require 'net/http' require 'uri' require 'json' api_key = "API_KEY" uri = URI.parse("https://api.ayrshare.com/api/ads/instagram/campaigns?accountId=1234567890") request = Net::HTTP::Get.new(uri) request["Authorization"] = "Bearer #{api_key}" response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == 'https') do |http| http.request(request) end puts JSON.parse(response.body) ``` ```json 200: Success theme={"system"} { "status": "success", "campaigns": [ { "accountId": "8501481455633", "boostedObjectId": "67263320208733", "budgetRemaining": 0, "buyingType": "AUCTION", "campaignId": "1202176707545833", "configuredStatus": "ACTIVE", "created": "2025-03-26T19:42:43-0400", "dailyBudget": 0, "effectiveStatus": "ACTIVE", "endDate": "2026-03-28T18:30:00-0400", "lifetimeBudget": 0, "metrics": {}, "name": "API Post - DE6gpw8kxlonHy6eb33 - 2025-03-26T23:42:43", "spendCap": 0, "startDate": "2026-03-26T18:30:00-0400", "status": "ACTIVE" } ], "count": 1, "lastUpdated": "2025-03-27T00:57:15.339Z", "nextUpdate": "2025-03-27T01:08:15.339Z" } ``` ```json 400: Ad campaigns error theme={"system"} { "action": "get ad campaigns", "status": "error", "code": 367, "message": "Error getting ad campaigns. Please verify you have an active ad campaign account." } ``` # Cities Source: https://www.ayrshare.com/docs/apis/ads/instagram/get-ad-cities GET /ads/instagram/cities Find cities by name for Instagram ad targeting. Results include Facebook's key, region and regionId, supportsCity and supportsRegion flags, and subcities. Search for cities to be used for targeting when boosting an ad.
  • Cities use Facebook's `key` identifier and support radius targeting.
  • Each city result includes its parent region as `region` (the region name) and `regionId` (the region's numeric identifier), so you can target the city's region when it is available.
  • If a returned city has `supportsRegion` `true`, the region for this city is available for targeting. If `supportsCity` is `true`, this city is available for targeting.
  • Some locations may be returned as `subcity` with additional hierarchy fields.
## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` **Profile-Key** `string` [Profile Key](/docs/apis/overview#profile-key-format) of a User Profile. Format: `Profile-Key: PROFILE_KEY` ## Query Parameters **search** `string` The search string to filter cities. Provide a partial or full city name. **limit** `number` (default: 25) Maximum number of cities to return. ```bash cURL theme={"system"} curl \ -H "Authorization: Bearer API_KEY" \ -X GET "https://api.ayrshare.com/api/ads/instagram/cities?search=Manhattan" ``` ```javascript JavaScript theme={"system"} const API_KEY = "API_KEY"; fetch("https://api.ayrshare.com/api/ads/instagram/cities?search=Manhattan", { method: "GET", headers: { "Authorization": `Bearer ${API_KEY}` } }) .then((res) => res.json()) .then((json) => console.log(json)) .catch(console.error); ``` ```python Python theme={"system"} import requests headers = {'Authorization': 'Bearer API_KEY'} r = requests.get('https://api.ayrshare.com/api/ads/instagram/cities?search=Manhattan', headers=headers) print(r.json()) ``` ```php PHP theme={"system"} "https://api.ayrshare.com/api/ads/instagram/cities?search=Manhattan", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer API_KEY" ], ]); $response = curl_exec($curl); curl_close($curl); echo $response; ?> ``` ```csharp C# theme={"system"} using System; using System.Net.Http; using System.Threading.Tasks; class Program { static async Task Main() { var client = new HttpClient(); client.DefaultRequestHeaders.Add("Authorization", "Bearer API_KEY"); var response = await client.GetAsync("https://api.ayrshare.com/api/ads/instagram/cities?search=Manhattan"); var content = await response.Content.ReadAsStringAsync(); Console.WriteLine(content); } } ``` ```go Go theme={"system"} package main import ( "fmt" "io/ioutil" "net/http" ) func main() { client := &http.Client{} req, _ := http.NewRequest("GET", "https://api.ayrshare.com/api/ads/instagram/cities?search=Manhattan", nil) req.Header.Add("Authorization", "Bearer API_KEY") resp, _ := client.Do(req) body, _ := ioutil.ReadAll(resp.Body) fmt.Println(string(body)) } ``` ```java Java theme={"system"} import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; public class GetAdCities { public static void main(String[] args) throws Exception { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("https://api.ayrshare.com/api/ads/instagram/cities?search=Manhattan")) .header("Authorization", "Bearer API_KEY") .GET() .build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); System.out.println(response.body()); } } ``` ```ruby Ruby theme={"system"} require 'net/http' require 'json' uri = URI('https://api.ayrshare.com/api/ads/instagram/cities?search=Manhattan') req = Net::HTTP::Get.new(uri) req['Authorization'] = 'Bearer API_KEY' res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) } puts res.body ``` ```json 200: Success theme={"system"} { "status": "success", "cities": [ { "key": "2447439", "name": "Manhattan", "type": "city", "countryCode": "US", "countryName": "United States", "region": "Kansas", "regionId": 3859, "supportsRegion": true, "supportsCity": true }, { "key": "2703980", "name": "Manhattan", "type": "subcity", "countryCode": "US", "countryName": "United States", "region": "New York", "regionId": 3875, "supportsRegion": true, "supportsCity": true, "geoHierarchyLevel": "SUBCITY", "geoHierarchyName": "BOROUGH" } ], "count": 2 } ``` ```json 414: Location metadata error theme={"system"} { "action": "ads", "status": "error", "code": 414, "message": "Error getting ad location metadata. Please try again." } ``` # History Source: https://www.ayrshare.com/docs/apis/ads/instagram/get-ad-history GET /ads/instagram/history Retrieve daily Instagram ad spend and performance metrics, with goal-specific results, aggregated totals, and filters by account, ad, post, and date range. Retrieve detailed daily ad spend and performance metrics for your Instagram ads. This endpoint provides comprehensive analytics on your ads with daily breakdowns, goal-specific performance, and aggregated metrics. > [!TIP] > > The `history` array contains historical daily spend and performance metrics > for each ad. The data is as of 8 AM EST. If real-time spend data is needed or spend has not yet occurred, > please use the [ads endpoint](/docs/apis/ads/instagram/get-boosted-ads). > > The ad will **NOT** be returned if no spend data is available or spend has not yet occurred. > > When no ad has spend in the date range, the response is still a success: `history` is > empty and `warnings` holds one entry with code `401`.
  • Metrics are calculated from Instagram's reporting API and may have slight delays (up to 24 hours).
  • The `byGoalType` and `byGoalTitle` sections group performance metrics by goal objectives.
  • Use either `adId` or `postId` filters to narrow results to specific campaigns.
  • Each day's `spend` is in the ad account's currency, shown in `currency`. `spendUsd` is the same spend in US dollars.
  • Non-USD spend is converted to USD at that day's exchange rate from Open Exchange Rates. `totalSpend`, `cpm`, `cpp`, `cpc`, `costPerResult` and the goal `spend` totals are in US dollars.
  • `totalSpendByCurrency` gives the total spend in each currency before conversion. `unconvertedRows` counts the days not converted yet: they count at face value in `totalSpend`, and their `spendUsd` is `null`.
  • Spend from before October 2026 may show `currency: "USD"` even when the ad account uses another currency, and is counted in `totalSpend` as recorded.
  • The `goalPerformance` section highlights your best-performing ad objectives.
## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` **Profile-Key** `string` [Profile Key](/docs/apis/overview#profile-key-format) of a User Profile. Format: `Profile-Key: PROFILE_KEY` ## Query Parameters **accountId** `string` Filter results to a specific account ID. **adId** `string` Filter results to a specific ad ID. **postId** `string` Filter results to ads associated with a specific [Ayrshare post ID](/docs/apis/overview#ayrshare-post-id). **socialPostId** `string` Filter results to ads associated with a specific [Instagram social post ID](/docs/apis/overview#social-post-id). **limit** `number` (default: 25) Retrieve a maximum of `limit` ads. Maximum is 300; higher values are capped at 300. **startDate** `string` (default: 30 days ago) Start date for the reporting period in ISO 8601 format (default: 30 days ago) **endDate** `string` (default: today) End date for the reporting period in ISO 8601 format (default: today) ```bash cURL theme={"system"} curl \ -H "Authorization: Bearer API_KEY" \ -X GET https://api.ayrshare.com/api/ads/instagram/history?accountId=1234567890 ``` ```javascript JavaScript theme={"system"} const API_KEY = "API_KEY"; fetch("https://api.ayrshare.com/api/ads/instagram/history?accountId=1234567890", { method: "GET", headers: { "Authorization": `Bearer ${API_KEY}` } }) .then((res) => res.json()) .then((json) => console.log(json)) .catch(console.error); ``` ```python Python theme={"system"} import requests headers = {'Authorization': 'Bearer API_KEY'} r = requests.get('https://api.ayrshare.com/api/ads/instagram/history', headers=headers) print(r.json()) ``` ```php PHP theme={"system"} "https://api.ayrshare.com/api/ads/instagram/history", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer API_KEY" ], ]); $response = curl_exec($curl); $err = curl_error($curl); curl_close($curl); if ($err) { echo "cURL Error: " . $err; } else { echo $response; } ?> ``` ```csharp C# theme={"system"} using System; using System.Net.Http; using System.Threading.Tasks; class Program { static async Task Main(string[] args) { var client = new HttpClient(); client.DefaultRequestHeaders.Add("Authorization", "Bearer API_KEY"); var response = await client.GetAsync("https://api.ayrshare.com/api/ads/instagram/history"); var content = await response.Content.ReadAsStringAsync(); Console.WriteLine(content); } } ``` ```go Go theme={"system"} package main import ( "fmt" "io/ioutil" "net/http" ) func main() { client := &http.Client{} req, _ := http.NewRequest("GET", "https://api.ayrshare.com/api/ads/instagram/history", nil) req.Header.Add("Authorization", "Bearer API_KEY") resp, err := client.Do(req) if err != nil { fmt.Println("Error:", err) return } defer resp.Body.Close() body, _ := ioutil.ReadAll(resp.Body) fmt.Println(string(body)) } ``` ```java Java theme={"system"} import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; public class GetAdSpend { public static void main(String[] args) { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("https://api.ayrshare.com/api/ads/instagram/history")) .header("Authorization", "Bearer API_KEY") .GET() .build(); try { HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); System.out.println(response.body()); } catch (Exception e) { e.printStackTrace(); } } } ``` ```ruby Ruby theme={"system"} require 'net/http' require 'json' require 'uri' uri = URI.parse('https://api.ayrshare.com/api/ads/instagram/history') request = Net::HTTP::Get.new(uri) request['Authorization'] = 'Bearer API_KEY' response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == 'https') do |http| http.request(request) end puts response.body ``` ```json 200: Success No Param theme={"system"} { "status": "success", "history": [ { "adId": "6683876017501", "adName": "API Post 1 - DE6gpw8kxlonHy6eb7L1 - 2025-03-30T17:11:26", "budgetDaily": 4, "budgetType": "daily", "currency": "USD", "deliveryStatus": "ACTIVE", "goal": { "description": "This goal seeks to increase engagement while ensuring that the ad reaches the maximum number of unique users. It balances visibility with engagement, showing the ad to as many different people as possible who may interact with it.", "title": "Get More Engagement", "type": "engagement" }, "isActive": true, "isComplete": false, "metrics": { "clicks": 3, "impressions": 1035, "reach": 1026, "frequency": 1.01, "ctr": 0.29, "cpm": 1.53, "cpp": 1.54, "cpc": 0.53 }, "network": "instagram", "postId": "DE6gpw8kxlonHy6eb7L1", "spend": 1.58, "spendDate": "2025-04-02T00:00:00.000Z", "spendUsd": 1.58, "status": "ACTIVE", "accountId": "274948332" }, { "adId": "120218167147110411", "adName": "API Post 2 - DE6gpw8kxlonHy6eb7L1 - 2025-03-30T16:41:15", "budgetDaily": 4, "budgetType": "daily", "currency": "USD", "deliveryStatus": "ACTIVE", "goal": { "description": "This goal seeks to increase engagement while ensuring that the ad reaches the maximum number of unique users. It balances visibility with engagement, showing the ad to as many different people as possible who may interact with it.", "title": "Get More Engagement", "type": "engagement" }, "isActive": true, "isComplete": false, "metrics": { "clicks": 4, "impressions": 1036, "reach": 1011, "frequency": 1.02, "ctr": 0.39, "cpm": 1.61, "cpp": 1.65, "cpc": 0.42 }, "network": "instagram", "postId": "DE6gpw8kxlonHy6eb7L1", "spend": 1.67, "spendDate": "2025-04-02T00:00:00.000Z", "spendUsd": 1.67, "status": "ACTIVE", "accountId": "274948332" } ], "dateRange": { "start": "2025-03-04T18:49:18.159Z", "end": "2025-04-03T18:49:18.159Z" }, "metrics": { "totalSpend": 3.25, "totalSpendByCurrency": { "USD": 3.25 }, "unconvertedRows": 0, "totalClicks": 7, "totalImpressions": 2071, "totalReach": 2037, "averageCTR": 0.34, "averageFrequency": 1.02, "cpm": 1.57, "cpp": 1.6, "cpc": 0.46, "costPerResult": 0.46, "byGoalType": { "engagement": { "spend": 3.25, "impressions": 2071, "clicks": 7, "reach": 2037, "count": 2, "ctr": 0.3380009657170449, "cpm": 1.5692901979719942, "cpp": 1.5954835542464407, "cpc": 0.4642857142857143 } }, "byGoalTitle": { "Get More Engagement": { "spend": 3.25, "impressions": 2071, "clicks": 7, "reach": 2037, "count": 2, "ctr": 0.3380009657170449, "cpm": 1.5692901979719942, "cpp": 1.5954835542464407, "cpc": 0.4642857142857143, "type": "engagement" } }, "goalPerformance": { "bestPerformingGoalType": { "type": "engagement", "clicks": 7, "spend": 3.25, "ctr": 0.34 }, "mostEfficientGoalType": { "type": "engagement", "cpc": 0.46, "spend": 3.25 } }, "count": 2, "firstDay": "2025-04-02T00:00:00.000Z", "lastDay": "2025-04-02T00:00:00.000Z" } } ``` ```json 200: Success by Ad ID theme={"system"} { "status": "success", "history": [ { "accountId": "274948332", "adId": "6683876017505", "adName": "API Post 13 - DE6gpw8kxlonHy6eb7Lo - 2025-03-30T17:11:26", "budgetDaily": 4, "budgetType": "", "currency": "USD", "deliveryStatus": "ACTIVE", "socialPostId": "17890000000000001", "goal": { "description": "This goal seeks to increase engagement while ensuring that the ad reaches the maximum number of unique users. It balances visibility with engagement, showing the ad to as many different people as possible who may interact with it.", "title": "Get More Engagement", "type": "engagement" }, "isActive": true, "isComplete": false, "metrics": { "clicks": 2, "cpc": 0.35, "cpp": 1.6, "cpm": 1.55, "ctr": 0.44, "frequency": 1.03, "impressions": 452, "reach": 438 }, "network": "instagram", "postId": "DE6gpw8kxlonHy6eb7Lo", "spend": 0.7, "spendDate": "2025-04-03T00:00:00.000Z", "spendUsd": 0.7, "status": "ACTIVE", "igPostId": "17890000000000001" }, { "accountId": "274948332", "adId": "6683876017505", "adName": "API Post 13 - DE6gpw8kxlonHy6eb7Lo - 2025-03-30T17:11:26", "budgetDaily": 4, "budgetType": "daily", "currency": "USD", "deliveryStatus": "ACTIVE", "socialPostId": "17890000000000001", "goal": { "description": "This goal seeks to increase engagement while ensuring that the ad reaches the maximum number of unique users. It balances visibility with engagement, showing the ad to as many different people as possible who may interact with it.", "title": "Get More Engagement", "type": "engagement" }, "isActive": true, "isComplete": false, "metrics": { "clicks": 1, "cpc": 4.19, "cpp": 1.5, "cpm": 1.5, "ctr": 0.04, "frequency": 1, "impressions": 2793, "reach": 2793 }, "network": "instagram", "postId": "DE6gpw8kxlonHy6eb7Lo", "spend": 4.19, "spendDate": "2025-04-02T00:00:00.000Z", "spendUsd": 4.19, "status": "ACTIVE", "igPostId": "17890000000000001" } ], "dateRange": { "start": "2025-03-04T18:49:18.159Z", "end": "2025-04-03T18:49:18.159Z", "adId": "6683876017505" }, "metrics": { "totalSpend": 4.89, "totalSpendByCurrency": { "USD": 4.89 }, "unconvertedRows": 0, "totalClicks": 3, "totalImpressions": 3245, "totalReach": 3231, "averageCTR": 0.09, "averageFrequency": 1.02, "cpm": 1.51, "cpp": 1.51, "cpc": 1.63, "costPerResult": 1.63, "byGoalType": { "engagement": { "spend": 4.890000000000001, "impressions": 3245, "clicks": 3, "reach": 3231, "count": 2, "ctr": 0.09244992295839753, "cpm": 1.50693374422188, "cpp": 1.5134633240482824, "cpc": 1.6300000000000001 } }, "byGoalTitle": { "Get More Engagement": { "spend": 4.890000000000001, "impressions": 3245, "clicks": 3, "reach": 3231, "count": 2, "ctr": 0.09244992295839753, "cpm": 1.50693374422188, "cpp": 1.5134633240482824, "cpc": 1.6300000000000001, "type": "engagement" } }, "goalPerformance": { "bestPerformingGoalType": { "type": "engagement", "clicks": 3, "spend": 4.89, "ctr": 0.09 }, "mostEfficientGoalType": { "type": "engagement", "cpc": 1.63, "spend": 4.89 } }, "count": 2, "firstDay": "2025-04-02T00:00:00.000Z", "lastDay": "2025-04-03T00:00:00.000Z" } } ``` ```json 200: No spend data theme={"system"} { "status": "success", "history": [], "dateRange": { "start": "2025-03-04T18:49:18.159Z", "end": "2025-04-03T18:49:18.159Z", "adId": "6683876017505" }, "metrics": { "totalSpend": 0, "totalSpendByCurrency": {}, "unconvertedRows": 0, "totalClicks": 0, "totalImpressions": 0, "totalReach": 0, "averageCTR": 0, "averageFrequency": 0, "cpm": 0, "cpp": 0, "cpc": 0, "costPerResult": 0, "byGoalType": {}, "byGoalTitle": {}, "goalPerformance": { "bestPerformingGoalType": null, "mostEfficientGoalType": null }, "daysCount": 0, "firstDay": null, "lastDay": null }, "warnings": [ { "action": "ads", "status": "error", "code": 401, "message": "No spend data found for the ad." } ] } ``` ```json 400: Ad spend error theme={"system"} { "action": "ads", "status": "error", "code": 370, "message": "Error getting ads. Please try again or contact us if the issue persists.", "details": "Error retrieving ads spend data" } ``` # Insights Source: https://www.ayrshare.com/docs/apis/ads/instagram/get-ad-insights GET /ads/instagram/insights Pull live daily Instagram ad metrics for campaigns, ad sets or ads, split by age, gender, country or placement, with paging for long date ranges. Get live Instagram ad performance, one row per day, for your campaigns, ad sets or ads. Pick the metrics you want, split them by age, gender, country or placement, and page through long date ranges.
  • Numbers are read from Meta when you call, not from a nightly copy.
  • Use [History](/docs/apis/ads/instagram/get-ad-history) for the spend we bill. Use Insights for reporting and analysis.
  • All money values are in the ad account's currency, returned in `currency` on each row.
  • Campaigns must have been created through Ayrshare for this profile, and ad sets must be in such a campaign. An ad created through Ayrshare for another profile is refused. Other ad ids are read with this profile's linked Meta account, so Meta only returns ads that account can see.
  • One request covers one ad account. Ids from several ad accounts return code `101`, and so do several ad ids that Ayrshare has no record of. Ask for those one at a time.
## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` **Profile-Key** `string` [Profile Key](/docs/apis/overview#profile-key-format) of a User Profile. Format: `Profile-Key: PROFILE_KEY` ## Query Parameters **level** `string` *required* What each row reports on: `campaign`, `adSet` or `ad`. **ids** `string` Comma-separated campaign, ad set or ad ids, up to 50. Required for `level=campaign` and `level=adSet`. With `level=ad`, you can leave `ids` out and send `accountId` instead. This reports on the ads you created through Ayrshare for this profile in that ad account, not on every ad in it. It works for up to 50 such ads. For more, send their ids, or report by campaign. If none are found, the report is empty. **accountId** `string` The ad account id, with or without the `act_` prefix (`1234567890` or `act_1234567890`). Required with `level=ad` when `ids` is left out. **metrics** `string` Comma-separated metrics to return. Leave it out to get them all. See [Metrics](#metrics). **breakdowns** `string` Comma-separated breakdowns: `age`, `gender`, `country`, `placement`. Each day then has one row per value, or per combination with `age,gender`. Send one breakdown at a time, or `age,gender`; Meta refuses other mixes (such as `age,country`), so they return code `101`. **startDate** `string` (default: 30 days ago) First day, as `YYYY-MM-DD`. It can be up to 37 months ago. **endDate** `string` (default: today) Last day, as `YYYY-MM-DD`. **cursor** `string` The `nextCursor` from the previous response, to get the next page. ## Metrics | Metric | Type | Meaning | | - | - | - | | `spend` | number | Amount spent | | `impressions` | number | Times the ad was shown | | `reach` | number | People who saw the ad at least once | | `clicks` | number | Clicks | | `cpm` | number or `null` | Cost per 1,000 impressions | | `cpc` | number or `null` | Cost per click. `null` on a day with no clicks | | `ctr` | number or `null` | Click-through rate, in percent | | `actions` | array | Each action type Meta counted, with its count | | `conversionValue` | number | Total value of purchases | Instagram reports every metric in this table. One exception: Meta does not report `reach` for a report with `breakdowns` that starts more than 13 months ago, so `reach` is left out of those rows. On networks that don't report a metric, it is left out of the rows rather than returned as `0`, and asking for it returns code `101`. > [!NOTE] > > Meta counts some actions under more than one type, so don't add `actions` values together. For > example, `omni_purchase` already includes `purchase`. `conversionValue` counts each purchase once. ## Paging Each response returns up to 500 rows. If there are more, `nextCursor` is set. Send the same request again with `cursor` set to that value to get the next page. On the last page, `nextCursor` is `null`. ## Errors | Code | When | | - | - | | `101` | A parameter is missing or wrong, a breakdown or metric isn't supported, `startDate` is more than 37 months ago, more than 50 ids are sent, the ids are in more than one ad account, or Meta refuses the breakdown combination | | `400` | A campaign, ad set or ad isn't yours, or `accountId` isn't the ad account Ayrshare has on record for the ids you sent. For an ad set, `accountId` must also be the ad account Meta reports for it. For an ad with no Ayrshare record, `accountId` is not checked and the ad is read on its own | | `370` | Meta could not return the insights. Try again | ```bash cURL theme={"system"} curl \ -H "Authorization: Bearer API_KEY" \ -X GET "https://api.ayrshare.com/api/ads/instagram/insights?level=campaign&ids=120200000000000001&startDate=2026-09-01&endDate=2026-09-02" ``` ```javascript JavaScript theme={"system"} const API_KEY = "API_KEY"; const params = new URLSearchParams({ level: "campaign", ids: "120200000000000001", startDate: "2026-09-01", endDate: "2026-09-02", }); fetch(`https://api.ayrshare.com/api/ads/instagram/insights?${params}`, { method: "GET", headers: { Authorization: `Bearer ${API_KEY}` }, }) .then((res) => res.json()) .then((json) => console.log(json)) .catch(console.error); ``` ```python Python theme={"system"} import requests headers = {'Authorization': 'Bearer API_KEY'} params = { 'level': 'campaign', 'ids': '120200000000000001', 'startDate': '2026-09-01', 'endDate': '2026-09-02', } r = requests.get('https://api.ayrshare.com/api/ads/instagram/insights', headers=headers, params=params) print(r.json()) ``` ```json 200: Success theme={"system"} { "status": "success", "platform": "instagram", "level": "campaign", "startDate": "2026-09-01", "endDate": "2026-09-02", "insights": [ { "date": "2026-09-01", "level": "campaign", "id": "120200000000000001", "currency": "USD", "spend": 12.34, "impressions": 4000, "reach": 3100, "clicks": 80, "cpm": 3.085, "cpc": 0.15425, "ctr": 2, "actions": [ { "type": "link_click", "value": 75 }, { "type": "omni_purchase", "value": 3 } ], "conversionValue": 89.97 } ], "count": 1, "nextCursor": null } ``` ```json 200: Breakdowns theme={"system"} { "status": "success", "platform": "instagram", "level": "ad", "startDate": "2026-09-01", "endDate": "2026-09-01", "insights": [ { "date": "2026-09-01", "level": "ad", "id": "120210000000000001", "age": "25-34", "gender": "female", "currency": "EUR", "spend": 1.5, "impressions": 300 }, { "date": "2026-09-01", "level": "ad", "id": "120210000000000001", "age": "25-34", "gender": "male", "currency": "EUR", "spend": 1, "impressions": 200 } ], "count": 2, "nextCursor": null } ``` ```json 400: Not Yours theme={"system"} { "action": "ads", "status": "error", "code": 400, "message": "The ad is not found or not authorized." } ``` # Interests Source: https://www.ayrshare.com/docs/apis/ads/instagram/get-ad-interests GET /ads/instagram/interests Search Instagram ad targeting interests by keyword and get audience size estimates. Use returned interest IDs in the interests array to boost posts. Search for available Instagram ad targeting interests by keyword. Required for the [Boost Post](/docs/apis/ads/instagram/boost-post) endpoint. This endpoint helps you discover interest-based targeting options for your Instagram ads, along with audience size estimates.
  • Interest IDs can be used in the `interests` array when boosting posts.
  • Results are cached for 10 minutes.
  • Use specific, relevant search terms for best results.
  • Instagram may return different results based on your ad account's industry and region.
  • The `audienceSizeLowerBound` and `audienceSizeUpperBound` fields in the JSON response provide an estimated range of audience size for the interest and it is useful for targeting.
## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` **Profile-Key** `string` [Profile Key](/docs/apis/overview#profile-key-format) of a User Profile. Format: `Profile-Key: PROFILE_KEY` ## Query Parameters **search** `string` *required* The search query to retrieve Instagram ad interests. Don't forget to escape the search query, for example `Rhythm%20and%20blues%20music`. **limit** `number` (default: 100) Limit the number of ad interests returned. ```bash cURL theme={"system"} curl \ -H "Authorization: Bearer API_KEY" \ -X GET https://api.ayrshare.com/api/ads/instagram/interests?search=Rhythm%20and%20blues%20music ``` ```javascript JavaScript theme={"system"} const API_KEY = "API_KEY"; fetch("https://api.ayrshare.com/api/ads/instagram/interests?search=Rhythm%20and%20blues%20music", { method: "GET", headers: { "Authorization": `Bearer ${API_KEY}` } }) .then((res) => res.json()) .then((json) => console.log(json)) .catch(console.error); ``` ```python Python theme={"system"} import requests headers = {'Authorization': 'Bearer API_KEY'} r = requests.get('https://api.ayrshare.com/api/ads/instagram/interests?search=Rhythm%20and%20blues%20music', headers=headers) print(r.json()) ``` ```php PHP theme={"system"} "https://api.ayrshare.com/api/ads/instagram/interests?search=Rhythm%20and%20blues%20music", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer API_KEY" ], ]); $response = curl_exec($curl); curl_close($curl); echo $response; ?> ``` ```csharp C# theme={"system"} using System; using System.Net.Http; using System.Threading.Tasks; class Program { static async Task Main() { var client = new HttpClient(); client.DefaultRequestHeaders.Add("Authorization", "Bearer API_KEY"); var response = await client.GetAsync("https://api.ayrshare.com/api/ads/instagram/interests?search=Rhythm%20and%20blues%20music"); var content = await response.Content.ReadAsStringAsync(); Console.WriteLine(content); } } ``` ```go Go theme={"system"} package main import ( "fmt" "io/ioutil" "net/http" ) func main() { client := &http.Client{} req, _ := http.NewRequest("GET", "https://api.ayrshare.com/api/ads/instagram/interests?search=Rhythm%20and%20blues%20music", nil) req.Header.Add("Authorization", "Bearer API_KEY") resp, _ := client.Do(req) body, _ := ioutil.ReadAll(resp.Body) fmt.Println(string(body)) } ``` ```java Java theme={"system"} import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; public class GetAdInterests { public static void main(String[] args) throws Exception { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("https://api.ayrshare.com/api/ads/instagram/interests?search=Rhythm%20and%20blues%20music")) .header("Authorization", "Bearer API_KEY") .GET() .build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); System.out.println(response.body()); } } ``` ```ruby Ruby theme={"system"} require 'net/http' require 'json' uri = URI('https://api.ayrshare.com/api/ads/instagram/interests?search=Rhythm%20and%20blues%20music') req = Net::HTTP::Get.new(uri) req['Authorization'] = 'Bearer API_KEY' res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) } puts res.body ``` ```json 200: Success theme={"system"} { "status": "success", "interests": [ { "id": "6003195554098", "name": "Rhythm and blues music", "topic": "News and entertainment", "audienceSizeLowerBound": 669180255, "audienceSizeUpperBound": 786955980 }, { "id": "6003470511564", "name": "Do it yourself (DIY)", "topic": "Hobbies and activities", "audienceSizeLowerBound": 418387661, "audienceSizeUpperBound": 492023890 }, { "id": "6002926036121", "name": "Italy", "topic": "Travel, places and events", "audienceSizeLowerBound": 279313350, "audienceSizeUpperBound": 328472500 } ], "count": 3, "lastUpdated": "2025-03-27T01:01:38.547Z", "nextUpdate": "2025-03-27T01:12:38.547Z" } ``` ```json 400: Ad interests error theme={"system"} { "action": "get ad interests", "status": "error", "code": 368, "message": "Error getting ad interests. Please try again or contact us if the issue persists." } ``` # Regions Source: https://www.ayrshare.com/docs/apis/ads/instagram/get-ad-regions GET /ads/instagram/regions Search regions such as states and provinces for Instagram ad targeting, or call without parameters to list every country that supports region targeting. Search for regions (states, provinces, etc.) to be used for targeting when boosting an ad.
  • Regions use Facebook's `key` identifier. Use this `key` when boosting ads with region targeting.
  • If a returned region has supportsRegion true, you can target this region. If supportsCity is true, the region has city codes.
  • To get all countries that support region targeting, call this endpoint without query parameters.
  • Results may vary based on Facebook data availability.
## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` **Profile-Key** `string` [Profile Key](/docs/apis/overview#profile-key-format) of a User Profile. Format: `Profile-Key: PROFILE_KEY` ## Query Parameters **search** `string` The search string to filter regions. Provide a partial or full region name. **limit** `number` (default: 25) Maximum number of regions to return. Only used with `search`. Without `search`, up to 1000 regions are returned. ```bash cURL theme={"system"} # All countries that support region targeting curl \ -H "Authorization: Bearer API_KEY" \ -X GET "https://api.ayrshare.com/api/ads/instagram/regions" ``` ```bash cURL theme={"system"} curl \ -H "Authorization: Bearer API_KEY" \ -X GET "https://api.ayrshare.com/api/ads/instagram/regions?search=al" ``` ```javascript JavaScript theme={"system"} const API_KEY = "API_KEY"; fetch("https://api.ayrshare.com/api/ads/instagram/regions?search=al", { method: "GET", headers: { "Authorization": `Bearer ${API_KEY}` } }) .then((res) => res.json()) .then((json) => console.log(json)) .catch(console.error); ``` ```python Python theme={"system"} import requests headers = {'Authorization': 'Bearer API_KEY'} r = requests.get('https://api.ayrshare.com/api/ads/instagram/regions?search=al', headers=headers) print(r.json()) ``` ```php PHP theme={"system"} "https://api.ayrshare.com/api/ads/instagram/regions?search=al", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer API_KEY" ], ]); $response = curl_exec($curl); curl_close($curl); echo $response; ?> ``` ```csharp C# theme={"system"} using System; using System.Net.Http; using System.Threading.Tasks; class Program { static async Task Main() { var client = new HttpClient(); client.DefaultRequestHeaders.Add("Authorization", "Bearer API_KEY"); var response = await client.GetAsync("https://api.ayrshare.com/api/ads/instagram/regions?search=al"); var content = await response.Content.ReadAsStringAsync(); Console.WriteLine(content); } } ``` ```go Go theme={"system"} package main import ( "fmt" "io/ioutil" "net/http" ) func main() { client := &http.Client{} req, _ := http.NewRequest("GET", "https://api.ayrshare.com/api/ads/instagram/regions?search=al", nil) req.Header.Add("Authorization", "Bearer API_KEY") resp, _ := client.Do(req) body, _ := ioutil.ReadAll(resp.Body) fmt.Println(string(body)) } ``` ```java Java theme={"system"} import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; public class GetAdRegions { public static void main(String[] args) throws Exception { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("https://api.ayrshare.com/api/ads/instagram/regions?search=al")) .header("Authorization", "Bearer API_KEY") .GET() .build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); System.out.println(response.body()); } } ``` ```ruby Ruby theme={"system"} require 'net/http' require 'json' uri = URI('https://api.ayrshare.com/api/ads/instagram/regions?search=al') req = Net::HTTP::Get.new(uri) req['Authorization'] = 'Bearer API_KEY' res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) } puts res.body ``` ```json 200: Success theme={"system"} { "status": "success", "regions": [ { "key": "3843", "name": "Alabama", "type": "region", "countryCode": "US", "countryName": "United States", "supportsRegion": true, "supportsCity": true }, { "key": "3844", "name": "Alaska", "type": "region", "countryCode": "US", "countryName": "United States", "supportsRegion": true, "supportsCity": true } ], "count": 2 } ``` ```json 414: Location metadata error theme={"system"} { "action": "ads", "status": "error", "code": 414, "message": "Error getting ad location metadata. Please try again." } ``` # Boosted Ads Source: https://www.ayrshare.com/docs/apis/ads/instagram/get-boosted-ads GET /ads/instagram/ads Retrieve boosted Instagram ads by account, ad, or post ID, with status, Meta review results, current spend, real-time metrics, and shareable preview links. Retrieve ads that have been boosted for a specific Instagram post. This boosted ads endpoint provides detailed information about the boosted ads, including status, current spend, performance metrics (analytics), and preview links.
  • The metrics are real-time totals for the ads. If you need historical data, use the [get ad history endpoint](/docs/apis/ads/instagram/get-ad-history).
  • `review` is Meta's review of the ad: `status` is `pending` while Meta reviews it, `rejected` when Meta disapproved it or left policy feedback on it, and `approved` when the ad is active or pre-approved. `reasons` holds Meta's explanations, or the policy's name when Meta gives no text, and is empty when there is no feedback. `review` is `null` when neither the ad's status nor any feedback shows the review, for example an ad paused before review.
  • The `previewLink` URL allows you share the preview ad with your colleagues, so that they will see the ad for 24-hours across the various Instagram formats. Your colleagues' views of the ad will not be counted towards your ad spend.
## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` **Profile-Key** `string` [Profile Key](/docs/apis/overview#profile-key-format) of a User Profile. Format: `Profile-Key: PROFILE_KEY` ## Query Parameters **accountId** `string` *required* Retrieve all ads under the account ID. The ad account id, with or without the `act_` prefix (`1234567890` or `act_1234567890`). Required if `adId`, `socialPostId`, or `postId` is not set. **adId** `string` *required* Retrieve an ad with the ad ID. Required if `accountId`, `socialPostId`, or `postId` is not set. **socialPostId** `string` *required* Retrieve ads with the [Instagram social post ID](/docs/apis/overview#social-post-id). Required if `accountId`, `adId`, or `postId` is not set. **limit** `number` (default: 25) Retrieve a maximum of `limit` ads. Maximum is 300; higher values are capped at 300. **postId** `string` *required* Retrieve ads with the [Ayrshare post ID](/docs/apis/overview#ayrshare-post-id). Required if `accountId`, `adId`, or `socialPostId` is not set. **status** `string` (default: all) Filter ads by status of the ad. Omit this parameter to return ads of every status. Valid values are:
  • `active`
  • `paused`
  • `deleted`
  • `archived`
```bash cURL theme={"system"} curl \ -H "Authorization: Bearer API_KEY" \ -X GET https://api.ayrshare.com/api/ads/instagram/ads?adId=1234567890 ``` ```javascript JavaScript theme={"system"} const API_KEY = "API_KEY"; fetch("https://api.ayrshare.com/api/ads/instagram/ads?adId=1234567890", { method: "GET", headers: { "Authorization": `Bearer ${API_KEY}` } }) .then((res) => res.json()) .then((json) => console.log(json)) .catch(console.error); ``` ```python Python theme={"system"} import requests headers = {'Authorization': 'Bearer API_KEY'} r = requests.get('https://api.ayrshare.com/api/ads/instagram/ads?adId=1234567890', headers=headers) print(r.json()) ``` ```php PHP theme={"system"} true, CURLOPT_HTTPHEADER => $headers ]); $response = curl_exec($curl); if ($response === false) { echo 'Curl error: ' . curl_error($curl); } else { echo json_encode(json_decode($response), JSON_PRETTY_PRINT); } curl_close($curl); ``` ```csharp C# theme={"system"} using System; using System.Net.Http; using System.Threading.Tasks; namespace HistoryGETRequest_csharp { class History { static async Task Main(string[] args) { string API_KEY = "API_KEY"; string url = "https://api.ayrshare.com/api/ads/instagram/ads?adId=1234567890"; using (var client = new HttpClient()) { client.DefaultRequestHeaders.Add("Authorization", "Bearer " + API_KEY); try { var response = await client.GetStringAsync(url); Console.WriteLine(response); } catch (HttpRequestException ex) { Console.WriteLine($"Error: {ex.Message}"); } } } } } ``` ```json 200: Success with Ad ID theme={"system"} { "status": "success", "ads": [ { "postId": "DE6gpw8kxlonHy6eb721", "ad": { "accountId": "274948331", "adId": "6683876017501", "budgetRemaining": 0, "created": "2025-03-30T10:11:34-0700", "creativeId": "1033490148629541", "dailyBudget": 0, "deliveryStatus": "ACTIVE", "endDate": "2025-04-03T22:30:00.000Z", "socialPostId": "106638148652329", "goal": { "title": "Get More Engagement", "description": "This goal seeks to increase engagement while ensuring that the ad reaches the maximum number of unique users. It balances visibility with engagement, showing the ad to as many different people as possible who may interact with it.", "type": "engagement" }, "isComplete": false, "lifetimeBudget": 0, "metrics": { "spend": 9.21, "impressions": 6495, "reach": 6272, "clicks": 3, "ctr": 0.046189, "cpm": 1.418014, "cpp": 1.468431, "frequency": 1.035555, "uniqueClicks": 3, "uniqueCtr": 0.047832, "costPerUniqueClick": 3.07, "inlineLinkClicks": 0, "costPerInlineLinkClick": 0, "outboundClicks": 0, "costPerOutboundClick": 0, "websiteCtr": [], "accountCurrency": "USD", "accountName": "John Doe", "accountId": "274948331" }, "name": "API Post - DE6gpw8kxlonHy6eb7L1 - 2025-03-30T17:11:26", "previewLink": "https://fb.me/22Cn31wXlzhxOC1", "review": { "reasons": [], "status": "approved" }, "spend": 9.21, "startDate": "2025-03-30T17:11:32.000Z", "status": "ACTIVE", "targeting": { "ageMax": 65, "ageMin": 18, "geoLocations": { "countries": [ "US" ], "locationTypes": [ "home", "recent" ] }, "interests": [ { "id": "6003195554098", "name": "Rhythm and blues music" } ] } } } ], "count": 1 } ``` ```json 400: Boosted ads error theme={"system"} { "action": "ads", "status": "error", "code": 370, "message": "Error getting ads. Please try again or contact us if the issue persists." } ``` # DSA Recommendations Source: https://www.ayrshare.com/docs/apis/ads/instagram/get-dsa-recommendations GET /ads/instagram/dsaRecommendations Get Digital Services Act (DSA) beneficiary and payor recommendations for the Meta ad account behind your Instagram ads, required for ads targeting the EU. Get Digital Services Act (DSA) recommendations for the Meta ad account used for your Instagram ads. As part of the requirements set forth by the European Union (EU) Digital Services Act (DSA), [Facebook requires ads targeting](https://www.facebook.com/business/help/605021638170961) any part of the EU to provide string values defining the beneficiary and payor of the ad being created. This endpoint provides a list of recommended DSA beneficiaries and payors that can be used when boosting posts. These fields may be used in [`dsaBeneficiary`](/docs/apis/ads/instagram/boost-post#param-dsa-beneficiary) and [`dsaPayor`](/docs/apis/ads/instagram/boost-post#param-dsa-payor).
  • Outputs a list of strings (maximum 25) that Facebook has identified to likely be the beneficiary/payor, based on recent activity of the ad account.
  • These recommendations can be used as values for the `dsaBeneficiary` and `dsaPayor` parameters in the [Boost Post](/docs/apis/ads/instagram/boost-post) endpoint.
  • Both `dsaBeneficiary` and `dsaPayor` must be set together if either is provided when boosting posts.
## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` **Profile-Key** `string` [Profile Key](/docs/apis/overview#profile-key-format) of a User Profile. Format: `Profile-Key: PROFILE_KEY` ## Query Parameters **accountId** `string` *required* The ID of the Meta ad account to get DSA recommendations for. The account ID can be retrieved from the [ad accounts endpoint](/docs/apis/ads/instagram/get-ad-accounts). ```bash cURL theme={"system"} curl \ -H "Authorization: Bearer API_KEY" \ -X GET https://api.ayrshare.com/api/ads/instagram/dsaRecommendations?accountId=1234567890 ``` ```javascript JavaScript theme={"system"} const API_KEY = "API_KEY"; fetch("https://api.ayrshare.com/api/ads/instagram/dsaRecommendations?accountId=1234567890", { method: "GET", headers: { "Authorization": `Bearer ${API_KEY}` } }) .then((res) => res.json()) .then((json) => console.log(json)) .catch(console.error); ``` ```python Python theme={"system"} import requests headers = {'Authorization': 'Bearer API_KEY'} r = requests.get('https://api.ayrshare.com/api/ads/instagram/dsaRecommendations?accountId=1234567890', headers=headers) print(r.json()) ``` ```php PHP theme={"system"} "https://api.ayrshare.com/api/ads/instagram/dsaRecommendations?accountId=1234567890", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer API_KEY" ], ]); $response = curl_exec($curl); curl_close($curl); echo $response; ?> ``` ```csharp C# theme={"system"} using System; using System.Net.Http; using System.Threading.Tasks; class Program { static async Task Main() { var client = new HttpClient(); client.DefaultRequestHeaders.Add("Authorization", "Bearer API_KEY"); var response = await client.GetAsync("https://api.ayrshare.com/api/ads/instagram/dsaRecommendations?accountId=1234567890"); var content = await response.Content.ReadAsStringAsync(); Console.WriteLine(content); } } ``` ```go Go theme={"system"} package main import ( "fmt" "io/ioutil" "net/http" ) func main() { client := &http.Client{} req, _ := http.NewRequest("GET", "https://api.ayrshare.com/api/ads/instagram/dsaRecommendations?accountId=1234567890", nil) req.Header.Add("Authorization", "Bearer API_KEY") resp, _ := client.Do(req) body, _ := ioutil.ReadAll(resp.Body) fmt.Println(string(body)) } ``` ```java Java theme={"system"} import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; public class GetDsaRecommendations { public static void main(String[] args) throws Exception { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("https://api.ayrshare.com/api/ads/instagram/dsaRecommendations?accountId=1234567890")) .header("Authorization", "Bearer API_KEY") .GET() .build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); System.out.println(response.body()); } } ``` ```ruby Ruby theme={"system"} require 'net/http' require 'json' uri = URI('https://api.ayrshare.com/api/ads/instagram/dsaRecommendations?accountId=1234567890') req = Net::HTTP::Get.new(uri) req['Authorization'] = 'Bearer API_KEY' res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) } puts res.body ``` ```json 200: Success theme={"system"} { "status": "success", "dsaRecommendations": [ "Beneficiary 1", "Beneficiary 2", "Beneficiary 3" ], "count": 3 } ``` ```json 413: DSA Recommendations Error theme={"system"} { "action": "ads", "status": "error", "code": 413, "message": "There was an error getting the DSA recommendations. Please try again." } ``` # Update Ad Source: https://www.ayrshare.com/docs/apis/ads/instagram/put-ad-update PUT /ads/instagram/ads Change an Instagram boosted ad's status with adId via PUT /ads/instagram/ads. Learn why attachTo campaigns keep their status and when untracked ads fail. Update the status of a boosted ad to make it active, paused, deleted, or archived. * For a boost added to your own campaign or ad set with `attachTo`, the new status applies only to what the boost created: the ad, and the ad set when the boost created it. The campaign or ad set you named in `attachTo` keeps its own status. The ad runs only while the campaign and ad set above it are active. See [Boost into an Existing Campaign or Ad Set](/docs/apis/ads/instagram/boost-post#attach-to). * An ad that Ayrshare has no record of can be paused, deleted or archived, but not set to `active`. That request returns a `400`. Contact support with the ad id. ## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` **Profile-Key** `string` [Profile Key](/docs/apis/overview#profile-key-format) of a User Profile. Format: `Profile-Key: PROFILE_KEY` ## Body Parameters **adId** `string` *required* The boosted ad ID to update. **status** `string` *required* The status of the ad. Values: `active`, `paused`, `deleted`, or `archived`. ```bash cURL theme={"system"} curl \ -H "Authorization: Bearer API_KEY" \ -H "Content-Type: application/json" \ -X PUT https://api.ayrshare.com/api/ads/instagram/ads \ -d '{"adId": "1234567890", "status": "active"}' ``` ```javascript JavaScript theme={"system"} const API_KEY = "API_KEY"; fetch("https://api.ayrshare.com/api/ads/instagram/ads", { method: "PUT", headers: { "Content-Type": "application/json", "Authorization": `Bearer ${API_KEY}` }, body: JSON.stringify({ adId: "1234567890", status: "active" }) }) .then((res) => res.json()) .then((json) => console.log(json)) .catch(console.error); ``` ```python Python theme={"system"} import requests headers = {'Authorization': 'Bearer API_KEY'} r = requests.put('https://api.ayrshare.com/api/ads/instagram/ads', headers=headers, json={"adId": "1234567890", "status": "active"}) print(r.json()) ``` ```php PHP theme={"system"} "https://api.ayrshare.com/api/ads/instagram/ads", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer API_KEY", "Content-Type: application/json" ], CURLOPT_CUSTOMREQUEST => "PUT", CURLOPT_POSTFIELDS => json_encode(["adId" => "1234567890", "status" => "active"]) ]); $response = curl_exec($curl); $err = curl_error($curl); curl_close($curl); if ($err) { echo "cURL Error: " . $err; } else { echo $response; } ?> ``` ```csharp C# theme={"system"} using System; using System.Net.Http; using System.Text; using Newtonsoft.Json; using System.Threading.Tasks; class Program { static async Task Main(string[] args) { var client = new HttpClient(); client.DefaultRequestHeaders.Add("Authorization", "Bearer API_KEY"); var response = await client.PutAsync("https://api.ayrshare.com/api/ads/instagram/ads", new StringContent(JsonConvert.SerializeObject(new { adId = "1234567890", status = "active" }), Encoding.UTF8, "application/json")); var content = await response.Content.ReadAsStringAsync(); Console.WriteLine(content); } } ``` ```go Go theme={"system"} package main import ( "strings" "fmt" "io/ioutil" "net/http" ) func main() { client := &http.Client{} req, _ := http.NewRequest("PUT", "https://api.ayrshare.com/api/ads/instagram/ads", strings.NewReader(`{"adId":"1234567890","status":"active"}`)) req.Header.Add("Content-Type", "application/json") req.Header.Add("Authorization", "Bearer API_KEY") resp, err := client.Do(req) if err != nil { fmt.Println("Error:", err) return } defer resp.Body.Close() body, _ := ioutil.ReadAll(resp.Body) fmt.Println(string(body)) } ``` ```java Java theme={"system"} import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; public class GetAdSpend { public static void main(String[] args) { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("https://api.ayrshare.com/api/ads/instagram/ads")) .header("Authorization", "Bearer API_KEY") .header("Content-Type", "application/json") .PUT(HttpRequest.BodyPublishers.ofString("{\"adId\":\"1234567890\",\"status\":\"active\"}")) .build(); try { HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); System.out.println(response.body()); } catch (Exception e) { e.printStackTrace(); } } } ``` ```ruby Ruby theme={"system"} require 'net/http' require 'json' require 'uri' uri = URI.parse('https://api.ayrshare.com/api/ads/instagram/ads') request = Net::HTTP::Put.new(uri) request['Content-Type'] = 'application/json' request['Authorization'] = 'Bearer API_KEY' request.body = JSON.generate({ adId: "1234567890", status: "active" }) response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == 'https') do |http| http.request(request) end puts response.body ``` ```json 200: Success theme={"system"} { "status": "success", "adId": "6691084804905", "requestedStatus": "ACTIVE" } ``` ```json 400: Ad update error theme={"system"} { "action": "request", "status": "error", "code": 101, "message": "Missing or incorrect parameters. Please verify with the docs. https://www.ayrshare.com/docs/apis", "details": "Invalid status: actives. Status must be either 'active', 'paused', 'deleted', or 'archived'." } ``` # Ads API: Boost Facebook and Instagram Posts and Manage Google Ads Source: https://www.ayrshare.com/docs/apis/ads/overview Boost your Meta posts and create Google Ads using Ayrshare's API. ## Introduction to the Ads API The Ayrshare Ads API provides programmatic access to create, manage, and analyze social media ads. The Ayrshare Ads API supports **Facebook / Instagram and Google**. For Google, create and manage [Search campaigns](/docs/apis/ads/google/search-campaigns) or [Performance Max campaigns](/docs/apis/ads/google/performance-max-campaigns). For Facebook and Instagram, the Facebook Ads API, also known as the Facebook Marketing API, allows you to transform existing posts into paid advertisements. This includes boosting posts (transforming a post into an ad), managing ads, tracking performance, and analyzing ad spend. Ads created and updated via Ayrshare can always be found in the Facebook Ads Manager. Ayrshare handles all of the settings at the ad, ad set, and campaign levels. There is never a need to manually edit any of the settings directly in the Facebook Ads Manager. > [!NOTE] > > For Instagram connection requirements and request examples, see [Instagram Ads](/docs/apis/ads/instagram/boost-post). The walkthrough below uses Facebook. > [!NOTE] > > 1. The Ads capabilities require the paid Ads add-on which can be enabled on the > Account page of the [web dashboard](https://app.ayrshare.com/account). > > 2. The ad account you boost from must have a [payment method](https://business.facebook.com/billing_hub/payment_settings/?placement=ads_manager) attached at Facebook (Meta) before you create an ad. One saved only on the business portfolio is not enough. For Meta's other account prerequisites, see [Boost Post](/docs/apis/ads/facebook/boost-post). > > 3. User Profiles linked with a Facebook Page prior to April 1, 2025 should be > re-linked to enable ads. For more information, see our [Facebook Ads API guide](https://www.ayrshare.com/blog/facebook-ads-api-boosting-with-the-marketing-api/). ## Supported Platforms Each Ads endpoint takes the network as its path segment, for example `/ads/facebook/accounts` or `/ads/instagram/accounts`. `code: 101` means the network is not an Ads network or does not offer that endpoint. `code: 424` means ads on that network are temporarily unavailable. | Endpoint | Platforms | | - | - | | `GET /ads/{platform}/accounts` | `facebook`, `instagram`, `google` | | `GET /ads/{platform}/interests` | `facebook`, `instagram` | | `GET /ads/{platform}/campaigns` | `facebook`, `instagram` | | `POST /ads/{platform}/campaigns` | `google` | | `GET /ads/{platform}/campaigns/{id}` | `google` | | `PUT /ads/{platform}/campaigns/{id}` | `google` | | `POST /ads/{platform}/boost` | `facebook`, `instagram` | | `GET /ads/{platform}/ads` | `facebook`, `instagram` | | `PUT /ads/{platform}/ads` | `facebook`, `instagram` | | `GET /ads/{platform}/history` | `facebook`, `instagram` | | `GET /ads/{platform}/insights` | `facebook`, `instagram`, `google` | | `GET /ads/{platform}/dsaRecommendations` | `facebook`, `instagram` | | `GET /ads/{platform}/regions` | `facebook`, `instagram` | | `GET /ads/{platform}/cities` | `facebook`, `instagram` | ## Create an Ad Flow ![Ads Flow](https://mintcdn.com/ayrshare-docs/Nmrhj2Gh7WSf62Bh/images/apis/ads/ads-flow.webp?fit=max\&auto=format\&n=Nmrhj2Gh7WSf62Bh\&q=85\&s=3e57ee2134ddc3bb0937acf5b3d31781) Boosting a post on Facebook is a quick and simple way to turn one of your existing posts into an ad. Here is a high-level flow of how to implement it in your platform: **1. Choose the Facebook Ad Account** Your user will choose the Facebook ad account which has all the billing details already configured. > [card] [Get Facebook Ad Accounts](/docs/apis/ads/facebook/get-ad-accounts) **2. Select the Post** Select a post from your Facebook page. It can be a photo, video, status update, etc. > **Tip:** Posts with good organic engagement (likes, shares, comments) tend to perform better when boosted. **3. Click “Boost Post”** Your user will click a button such as "Boost Post" in your platform to start setting up the ad. > [card] [Boost a post by submitting it to Facebook's ad platform](/docs/apis/ads/facebook/boost-post) **4. Set the Goal, Audience, Budget, & Duration** The user can set a number of parameters.
  • Choose the goal strategy for this boosted post by selecting a goal.
  • Customize the audience based on age, gender, location, interests, etc.
  • Choose how much to spend total.
  • Set the time period to run the ad.
**5. Review & Boost** Double-check all the settings. If everything looks good, the user will click a button such as **"Boost Post Now"** button. **6. Ad Review & Launch** Facebook will review the ad — this usually takes anywhere from a few minutes to a few hours. Once approved, the boosted post goes live and runs according to the setup. **7. Track Spend & Performance** Monitor the following: * Reach * Engagement * Clicks * Spend * Budget Remaining > [card] [Get Facebook Ad Spend & Analytics](/docs/apis/ads/facebook/get-ad-history) ## Ads API Best Practices 1. **Budget Management**: Start with small test budgets (e.g., \$5-10/day) to optimize performance before scaling. 2. **Ad Duration**: Facebook requires a minimum ad runtime of approximately 30 hours between start and end times. 3. **Interest Targeting**: Choose 2-5 relevant interests for optimal targeting precision. 4. **Creative Guidelines**: Use high-quality images and concise messaging to improve engagement rates. 5. **Performance Monitoring**: Regularly check your ad performance and adjust targeting or creative elements as needed. For detailed information about each endpoint, including request parameters, response formats, and examples, please refer to the individual API endpoint documentation pages. # Count of Instagram Followers Online Source: https://www.ayrshare.com/docs/apis/analytics/instagram-follower-count GET /analytics/getInstagramOnlineFollowers Retrieve the historical count of your Instagram followers online per hour for a given day, so you can optimize posting times for maximum engagement. Retrieve the total historical count of your Instagram followers online per hour, which allows you to optimize posting for maximum engagement.
  • Not available on IG Users with fewer than 100 followers.
  • Analytics data only available for the last 30 days.
  • The time period for a requested day is the previous day at T07:00:00.000Z until the current day at T06:59:59.999Z in UTC.
## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` **Profile-Key** `string` [Profile Key](/docs/apis/overview#profile-key-format) of a User Profile. Format: `Profile-Key: PROFILE_KEY` ## Query Parameters **date** `string` *required* A day with format: `YYYY-MM-DD`, for example `2026-07-08`. A date with a time returns error `101`. **formatUTC** `boolean` (default: false) Format the output with UTC start and end dates. To get the default output, leave this parameter out. ```bash cURL theme={"system"} curl \ -H "Authorization: Bearer API_KEY" \ -X GET https://api.ayrshare.com/api/analytics/getInstagramOnlineFollowers?date=2023-12-03 ``` ```javascript JavaScript theme={"system"} const API_KEY = "API_KEY"; const date = "2023-12-03"; fetch(`https://api.ayrshare.com/api/analytics/getInstagramOnlineFollowers?date=${date}`, { method: "GET", headers: { Authorization: `Bearer ${API_KEY}` } }) .then((res) => res.json()) .then((json) => console.log(json)) .catch(console.error); ``` ```python Python theme={"system"} import requests headers = {'Authorization': 'Bearer API_KEY'} r = requests.get('https://api.ayrshare.com/api/analytics/getInstagramOnlineFollowers?date=2023-12-03', headers=headers) print(r.json()) ``` ```php PHP theme={"system"}
  • Bluesky
  • Facebook
  • Instagram
  • LinkedIn
  • Pinterest
  • Reddit
  • Snapchat
  • Threads
  • TikTok
  • YouTube
  • X/Twitter
  • The analytics may be retrieved for a specific post by providing either the Ayrshare post ID or the original post's Social Post ID. ## Social Analytics You can also get the account level social analytics, such as engagement, follower count and demographics, and account meta data. Use this endpoint if you want to get the analytics for an entire social account, rather than a specific post. Social account level analytics are available for the following social networks:
    • Facebook
    • Google Business Profile
    • Instagram
    • LinkedIn
    • Pinterest
    • Reddit
    • Snapchat
    • Threads
    • TikTok
    • YouTube
    • X/Twitter
    You may also choose to get aggregated summary or daily social analytics. ## Link Analytics You can also get analytics on a shortened link. This is great if you want to know how many clicks a link has received, either one sent via Ayrshare or manually published at the social network. Link analytics can be found in the /links endpoint. > [card] [Link Analytics](/docs/apis/links/link-analytics) > > Get analytics on a shortened link. # Analytics on a Post Source: https://www.ayrshare.com/docs/apis/analytics/post POST /analytics/post Retrieve real-time likes, impressions, views, and reactions for a post sent via Ayrshare, with multiplatform partial-success handling. > [!NOTE] > > **Targeting X/Twitter?** Starting March 31, 2026, all X operations require your own API credentials. After linking X via OAuth, include these 2 headers in your request: > > `X-Twitter-OAuth1-Api-Key` — Your API Key (Consumer Key) > > `X-Twitter-OAuth1-Api-Secret` — Your API Key Secret (Consumer Secret) > > **One-time setup per Ayrshare account.** You create one X Developer App and reuse the same API Key and Secret across every sub-profile / end-user you link. You do *not* create a new app per customer. > > Not linked yet? See the [full setup guide](/docs/dashboard/connect-social-accounts/x-twitter-byo-keys) to connect your X account. > > Your keys are never logged or stored by Ayrshare. Get real-time analytics such as likes, impressions, views, and reactions for a post that was sent via Ayrshare using the [Ayrshare Post ID](/docs/apis/overview#ayrshare-post-id). For analytics on posts originating outside of Ayrshare, see [Analytics by Social Post ID](/docs/apis/analytics/social-by-id). > [!WARNING] > > **Facebook: some reach and video metrics were retired by Meta (June 15, 2026).** Meta removed the unique-impression and 3-second video-view Insights metrics across all Graph API versions, so the Facebook `analytics` object no longer returns `impressionsUnique`, `impressionsFanUnique`, `impressionsOrganicUnique`, `impressionsPaidUnique`, or `videoViewsUnique`. For reach, use `totalMediaViewUnique` (the number of unique people this post was played or displayed to), Meta's Total Unique Media Views replacement for the retired unique-impression fields. `mediaView` remains available for total, non-unique views. Other commonly used fields such as `reactionsByType` and `videoViews` are unaffected. Reference: [Upcoming API Changes — June 15, 2026](/docs/whatsnew/upcoming-api-changes#june-15-2026). #### Additional Information
    • The following platforms are currently supported: Bluesky, Facebook Pages, Instagram, X/Twitter, LinkedIn, Pinterest, Reddit, Snapchat, Threads, TikTok, and YouTube.
    • Facebook and Instagram comment counts shown in the API may differ from counts displayed in the social apps. This occurs for two reasons: - Some users have privacy settings that hide their comments from non-friends or users without mutual connections. These private comments are still included in the total comment count. - There may be inconsistencies in the data reported by Meta.
    • Instagram igReelsAggregatedAllPlaysCount and playsCount shown in the API may differ from the counts displayed in the social apps. This can occur for a couple of reasons:
      • The post was promoted. Only organic plays are included in the API response.
      • There may be inconsistencies in the data reported by Meta.
    • X/Twitter Threads return as an Array of objects, with each object corresponding to a Tweet in the Thread.
    • YouTube can take 24-48 hours to process analytics for videos with few video views or channel followers.
    • TikTok Analytics:
      • TikTok can take 24-48 hours to update their analytics data, such as video views, demographics, likes, shares, and comments.
      • For TikTok analytics access, account owners must: 1. Publish at least one video. 2. Tap the "[Turn On](https://www.tiktok.com/feedback?id=7133058093574806018\&lang=en\&type=)" button on the Analytics page of their mobile TikTok app. 3. Have 100 followers to receive additional insights about viewers and content engagement.
      • TikTok does not return post analytics if the media contains copyrighted material or has been flagged for a copyright violation. Examples of copyrighted material can include audio on reels (TikTok will mute these videos). You can check in the TikTok app under Activity -> System Notifications.
      • Some TikTok analytics fields may be unavailable if the video has been inactive for more than 7 days. To retrieve this data, generate new activity on the video (view, like, comment, or share) and retry after 24-48 hours. If a video is not returned in the response, the reason is likely to be that the video has been filtered out due to violations, such as music copyright violation. These fields include:
        • `reach`
        • `fullVideoWatchedRate`
        • `totalTimeWatched`
        • `averageTimeWatched`
        • `impressionSources`
        • `audienceCountries`
    • LinkedIn personal (member) profiles now return an expanded post analytics matrix. The available member metrics are:
      • `impressionCount` — Impressions on the post
      • `uniqueImpressionsCount` — Unique members reached (LinkedIn `MEMBERS_REACHED`)
      • `likeCount` — Reaction count
      • `commentCount` — Comments count
      • `shareCount` — Reshares of the post
      • `engagement` — Organic clicks, likes, comments, and shares over impressions
      • `reactions` — Per-type reaction breakdown (when reactions are present)
      • For video posts: `videoViews`, `videoViewers`, and `videoWatchTimeMs`
      Video metrics are only returned for video posts and are unavailable more than one year after the post was created.
    • Instagram Story insights are only available for 24 hours, regardless of whether stories are archived or highlighted. Note:
      • Story media metrics with values less than `5` return as `0`.
      • For Stories created by users in Europe and Japan, the `replies` metric returns a value of `0`.
    • Facebook Story analytics are not available.
    • Pinterest analytics data (impressions, user followers, and clicks) becomes available after 24-72 hours.
    • For YouTube Shorts, views will return the number of times a Short starts to play or replay, with no minimum watch time requirement. Starting August 24, 2026, this counting applies to all YouTube video formats, including long-form videos and live streams.
    ## Multiplatform Reads & Partial Success A single [Ayrshare Post ID](/docs/apis/overview#ayrshare-post-id) can span several platforms. Ayrshare fans out one request ("leg") per platform and returns a **partial success** when some legs succeed and others fail — the healthy platforms' analytics are always returned, and each failed leg is enumerated in a top-level `errors[]` array.
    • **Some platforms succeed, some fail:** the response is HTTP `200` with `status: "partial"`. The healthy per-platform `analytics` blocks are returned as usual, and a top-level `errors[]` array lists every failed leg with its `platform`, `status`, `code`, `message`, and `id`.
    • **Every platform fails:** the response has `status: "error"` and the full `errors[]` array. The HTTP status is mapped from the representative top-level error code. Code `485` maps to HTTP `404`; other representative codes use their own mappings.
    • **All platforms succeed:** the response is unchanged — HTTP `200`, `status: "success"`, and no `errors[]` key.
    > [!WARNING] > > **Behavior change — inspect `errors[]`, don't branch on HTTP status.** Because a multiplatform read with a failing leg now returns HTTP `200` instead of collapsing the whole response to an error, integrators should always check for the presence of a top-level `errors[]` array to detect per-platform failures rather than relying on the HTTP status code alone. ### Expired or Unavailable Instagram Story Analytics An Instagram Story analytics leg that is expired or unavailable — so its insights cannot be retrieved — surfaces in `errors[]` with code [`485`](/docs/errors/errors-ayrshare#expired-or-unavailable-story-errors). A representative message is *"Instagram Story expired or unavailable — comments/insights cannot be retrieved."* Facebook Story analytics remain unavailable and are not covered by this behavior. If another platform succeeds, its analytics are still returned and the overall response is **HTTP `200`**. For an all-fail response, representative code `485` maps to HTTP `404`; other representative codes use their own mappings. Match on the `code` (`485`), not the exact message text. (See also the note above: Instagram Story insights are only available for 24 hours.) #### Example: Partial Success Response ```json 200: Partial Success theme={"system"} { "facebook": { "id": "1397547544885713_2159201585286968", "postUrl": "https://www.facebook.com/1397547544885713_2159201585286968", "analytics": { "commentsCount": 1, "likeCount": 23, "sharesCount": 12, "mediaView": 450 }, "lastUpdated": "2026-07-14T18:44:29.778Z", "nextUpdate": "2026-07-14T19:19:29.778Z" }, "status": "partial", "id": "IHvCLacgPc6hMU9IQ6oK", "errors": [ { "platform": "instagram", "status": "error", "code": 485, "message": "Instagram Story expired or unavailable — comments/insights cannot be retrieved.", "id": "17895695668004550" } ] } ``` ## Daily Post Analytics Send `daily: true` with `startDate` and `endDate` to get each post's analytics day by day for that period. Each day carries the last analytics Ayrshare collected for the post that day (UTC), with `fetchedAt` showing when. Each day's values are the post's totals as of `fetchedAt`, not the activity during that day. Days with nothing collected are left out. Without `daily`, the request works as described above. A network the profile is not linked to returns error `156`, and one that needs Meta identity verification returns error `326`, as they do without `daily`. After a network is relinked, days collected before the new link are left out. ```json theme={"system"} { "status": "success", "id": "Post ID", "instagram": { "id": "17895695668004550", "postUrl": "https://www.instagram.com/p/CBbVz8gnTqS/", "daily": [ { "date": "2026-10-01", "analytics": { "likeCount": 12, "commentsCount": 2 }, "fetchedAt": "2026-10-01T18:04:11.000Z" }, { "date": "2026-10-02", "analytics": { "likeCount": 15, "commentsCount": 3 }, "fetchedAt": "2026-10-02T17:58:40.000Z" } ] } } ``` With `searchPlatformId` and `postIds`, the platform's value is a list with one entry per post, in the order requested. An X or Threads thread is also a list, with one entry per post in the thread. `postUrl` is optional: it is included when a returned day has a post URL stored. A post with nothing collected in the period returns `daily: []` without `postUrl`. ### Daily analytics for many posts To get daily analytics for all of a profile's posts, for example in a daily report: 1. List the posts with [Get History](/docs/apis/history/get-history). History filters by publish date, not by when analytics were fetched, so pick a window that holds every post you report on, such as `lastDays=90`, not the report's dates. If that window holds more posts than `limit` returns, split it into shorter `startDate`/`endDate` windows. Each post's Social Post IDs are in `postIds[].id`, with the network in `postIds[].platform`. 2. For each network, call this endpoint with `daily: true`, the report's `startDate` and `endDate`, `searchPlatformId: true`, that one network in `platforms`, and up to 100 of its Social Post IDs in `postIds`. ```json theme={"system"} { "daily": true, "startDate": "2026-10-30T00:00:00Z", "endDate": "2026-10-31T00:00:00Z", "searchPlatformId": true, "platforms": ["instagram"], "postIds": ["17895695668004550", "17841405822304914"] } ``` A short period, such as the last day, keeps each response small. ## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` **Profile-Key** `string` [Profile Key](/docs/apis/overview#profile-key-format) of a User Profile. Format: `Profile-Key: PROFILE_KEY` ## Body Parameters **id** `string` *required* [Ayrshare Post ID](/docs/apis/overview#ayrshare-post-id) returned from the [/post endpoint](/docs/apis/post/post). This is the top-level Ayrshare `id` returned and not the social post ids in `postIds`. **platforms** `array` String array of platforms to retrieve analytics. Accepts an array of strings with values: ```json theme={"system"} { "platforms": [ "bluesky", "facebook", "instagram", "linkedin", "pinterest", "reddit", "snapchat", "threads", "tiktok", "twitter", "youtube" ] } ``` If platforms is not included, analytics will be returned for all social networks to which the post was sent. **daily** `boolean` (default: false) When set to `true`, returns the post's analytics day by day, from the analytics Ayrshare has already collected for it, instead of fetching them from the social network. Requires `startDate` and `endDate`. Also works with `searchPlatformId` and `postIds` for [Social Post IDs](/docs/apis/analytics/social-by-id). See [Daily Post Analytics](#daily-post-analytics). **startDate** `string` Required when `daily` is `true`. Start of the period, in ISO 8601 format: a date such as `2026-10-01` (read as midnight UTC), or a date-time with `Z` or an offset, such as `2026-10-01T00:00:00Z`. A date-time without `Z` or an offset returns error `101`. Includes analytics collected from this time. **endDate** `string` Required when `daily` is `true`. End of the period, in the same format as `startDate`, for example `2026-10-08T00:00:00Z`. Includes analytics collected before this time. The period can be at most 31 days. ```bash cURL theme={"system"} curl \ -H "Authorization: Bearer API_KEY" \ -H 'Content-Type: application/json' \ -d '{ "id": "Post ID", "platforms": ["bluesky", "facebook", "instagram", "linkedin", "pinterest", "snapchat", "threads", "tiktok", "twitter", "youtube"]}' \ -X POST https://api.ayrshare.com/api/analytics/post ``` ```javascript JavaScript theme={"system"} const API_KEY = "API_KEY"; fetch("https://api.ayrshare.com/api/analytics/post", { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${API_KEY}` }, body: JSON.stringify({ id: "Post ID", // required platforms: [ "bluesky", "facebook", "instagram", "linkedin", "pinterest", "snapchat", "threads", "tiktok", "twitter", "youtube" ] // optional }) }) .then((res) => res.json()) .then((json) => console.log(json)) .catch(console.error); ``` ```python Python theme={"system"} import requests payload = {'id': 'Post ID', 'platforms': ['bluesky', 'facebook', 'instagram', 'linkedin', 'pinterest', 'snapchat', 'threads', 'tiktok', 'twitter', 'youtube']} headers = {'Content-Type': 'application/json', 'Authorization': 'Bearer API_KEY'} r = requests.post('https://api.ayrshare.com/api/analytics/post', json=payload, headers=headers) print(r.json()) ``` ```php PHP theme={"system"} 'Post ID', // Replace with your actual Post ID 'platforms' => ['bluesky', 'facebook', 'instagram', 'linkedin', 'pinterest', 'snapchat', 'threads', 'tiktok', 'twitter', 'youtube'] // optional ]); $curl = curl_init($apiUrl); curl_setopt_array($curl, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_HTTPHEADER => $headers, CURLOPT_POSTFIELDS => $data ]); $response = curl_exec($curl); if ($response === false) { echo 'Curl error: ' . curl_error($curl); } else { echo json_encode(json_decode($response), JSON_PRETTY_PRINT); } curl_close($curl); ``` ```go Go theme={"system"} package main import ( "bytes" "encoding/json" "log" "net/http" ) func main() { message := map[string]interface{}{ "id": "Post ID", "platforms": []string{"bluesky", "facebook", "instagram", "linkedin", "pinterest", "snapchat", "threads", "tiktok", "twitter", "youtube"}, } bytesRepresentation, err := json.Marshal(message) if err != nil { log.Fatalln(err) } req, _ := http.NewRequest("POST", "https://api.ayrshare.com/api/analytics/post", bytes.NewBuffer(bytesRepresentation)) req.Header.Add("Content-Type", "application/json; charset=UTF-8") req.Header.Add("Authorization", "Bearer API_KEY") res, err := http.DefaultClient.Do(req) if err != nil { log.Fatal("Error:", err) } res.Body.Close() } ``` ```csharp C# theme={"system"} using System; using System.Net.Http; using System.Text; using System.Threading.Tasks; namespace PostAnalyticsPOSTRequest_csharp { class PostAnalytics { private static readonly HttpClient client = new HttpClient(); static async Task Main(string[] args) { string API_KEY = "API_KEY"; string url = "https://api.ayrshare.com/api/analytics/analytics/post"; client.DefaultRequestHeaders.Add("Authorization", "Bearer " + API_KEY); string json = "{\"id\": \"Post ID\"," + "\"platforms\": [\"bluesky\", \"facebook\", \"instagram\", \"linkedin\", \"pinterest\", \"snapchat\", \"tiktok\", \"twitter\", \"youtube\"]}"; try { var content = new StringContent(json, Encoding.UTF8, "application/json"); HttpResponseMessage response = await client.PostAsync(url, content); response.EnsureSuccessStatusCode(); string responseBody = await response.Content.ReadAsStringAsync(); Console.WriteLine(responseBody); } catch (HttpRequestException e) { Console.WriteLine($"Error: {e.Message}"); } } } } ``` > [!NOTE] > > When cumulative metrics (e.g., likes, comments, views) are temporarily unavailable from the social network, the API automatically backfills them from stored data. Two optional fields may appear in the per-platform `analytics` object: > > * **`backfilledFrom`** (string, ISO 8601) — Present when one or more cumulative metrics were substituted from stored data. The timestamp indicates when the stored data was last updated. > * **`recoveredFrom`** (string, ISO 8601) — Present when the entire analytics response was recovered from stored data due to a complete API failure. The timestamp indicates when the stored data was last updated. For Snapchat, where `analytics` is a list, `recoveredFrom` appears beside `analytics` instead. > > Stored data older than 4 days is considered stale and will not be used for backfill or recovery. > [!NOTE] > > **LinkedIn personal (member) analytics — re-link required.** Personal LinkedIn profiles linked before member post analytics shipped do not have the required analytics scopes. Analytics requests for those profiles return [error code `475`](/docs/errors/errors-ayrshare#linkedin-analytics-errors) ("re-link your LinkedIn profile to enable analytics"). The account owner must re-link their LinkedIn profile on the Social Accounts page to grant the new scopes. Allow a few minutes after re-linking for code `475` to clear (Ayrshare and LinkedIn both briefly cache the permission state, typically \~5-10 minutes). Posting is unaffected. > > LinkedIn member post counts are best-effort: the aggregate `shareCount` (RESHARE), `likeCount` (REACTION), and `commentCount` (COMMENT) totals may differ slightly from the numbers shown in the LinkedIn UI. ```json 200: Response {2, 24, 154, 236, 270, 321, 391, 407, 439, 453, 588, 741} theme={"system"} { "bluesky": { "id": "at://did:plc:n7atrjd22xgkmgwig6dzlhzd/app.bsky.feed.post/3lez7fwx457", // Bluesky Social Post ID "postUrl": "https://bsky.app/profile/madworlds.bsky.social/post/3lez7fwx457", "analytics": { "cid": "bafyreigh7nz7x6pl4atgsextqtcbh33mg66yqt3caeihutn7ojkmpdtnzm", // Bluesky Content ID "created": "2025-01-05T18:04:28.316Z", "id": "at://did:plc:n7atrjd22xgkmgwig6dzlhzd/app.bsky.feed.post/3lfb57nxgxs2e", // Bluesky Social Post ID "indexedAt": "2025-01-05T18:04:29.150Z", "labels": [], "likeCount": 3, "post": "The day is what you make it!", "postUrl": "https://bsky.app/profile/madworlds.bsky.social/post/3lez7fwx45723", "quoteCount": 2, "replyCount": 1, "repostCount": 1, "viewer": { // Information about the viewing user's relationship "threadMuted": false, // Whether the viewing user has muted the thread containing this content "embeddingDisabled": false // Whether embedding/sharing of this content is disabled for the viewing user } } }, /* If the FB page has fewer than 100 likes then not all analytics data available. */ "facebook": { "id": "1397547544885713_2159201585286968", // Facebook Social Post ID "postUrl": "https://www.facebook.com/1397547544885713_2159201585286968", "analytics": { "blueReelsPlayCount": 136, // Reels: The number of times your reel starts to play after an impression is already counted. This is defined as reels sessions with 1ms or more of playback and excludes replays. "commentsCount": 1, // Count of comment for the post // impressionsFanUnique / impressionsOrganicUnique / impressionsPaidUnique / impressionsUnique were retired by Meta (June 15, 2026); see the note at the top of this page. "likeCount": 23, // Count of likes for the post "likedBy": [ // Users who liked the post { "id": "7101149746568432", "name": "John Smith" } ], "mediaUrls": [ { "media": { "image": { "height": 405, "src": "https://scontent-lga3-1.xx.fbcdn.net/v/t", "width": 720 }, "source": "https://video-lga3-2.xx.fbcdn.net/o1/v/t2/f2/m69/AQN" }, "mediaType": "video", "type": "story" } ], "mediaView": 12, // The number of times this post was played or displayed. Content includes videos, posts, stories and ads. "mediaViewIsFromAds": 0, // The number of times this post was played or displayed from ads. "mediaViewIsFromFollowers": 0, // The number of times this post was played or displayed from followers. "postVideoAvgTimeWatched": 4470, // Reels: The average time (in ms) your reel was played, including any time spent replaying the reel during a single instance of it playing. Because this metric includes replays, this number could be greater than the total length of the reel "postVideoSocialActions": { // Reels: The number of comments, shares, and reactions on your reel "share": 1 }, "postVideoViewTime": 608011, // Reels: Total time (in ms) your reel was played, including any time spent replaying your reel. "reactions": { "like": 1, // Like reactions - The "like" reaction counts include both "like" and "care" reactions. "love": 1, // Love reactions "anger": 1, // Anger reactions "haha": 1, // Haha reactions "wow": 1, // Wow reactions "sorry": 1, // Sorry reactions "total": 6 // Total number of reactions }, "reactionsByType": 6, // The total types of reactions "sharesCount": 34, // Total number of times post shared "totalMediaViewUnique": 9, // Reach: the number of unique people this post was played or displayed to. /** The following fields are available for videos */ "totalVideo10SViews": 621, // Total number of times your video was viewed for 10 seconds or viewed to the end, whichever came first. "totalVideo10SViewsAutoPlayed": 564, // Number of times people clicked to play your video and viewed it for 10 seconds or viewed it to the end, whichever came first. "totalVideo10SViewsClickedToPlay": 46, // Number of times your video started automatically playing and people viewed it for 10 seconds, or viewed it to the end, whichever came first. "totalVideo10SViewsOrganic": 621, // Number of times your video was viewed for 10 seconds or viewed to the end, whichever came first, without a paid promotion. "totalVideo10SViewsPaid": 0, // Number of times your video was viewed for 10 seconds or viewed to the end, whichever came first, after a paid promotion. "totalVideo10SViewsSoundOn": 380, // Number of times your video sound was turned on and was viewed for 10 seconds or viewed to the end, whichever came first. "totalVideo10SViewsUnique": 621, // Number of unique people who viewed your video for 10 seconds or viewed to the end, whichever came first. "totalVideo15SViews": 525, // Total number of times your video was viewed for at least 15 seconds. "totalVideo60SExcludesShorterViews": 0, // Total number of times your video was viewed for 60 seconds only if the video is 60 seconds or longer. "totalVideoAvgTimeWatched": 8494, // Lifetime: Average time video viewed "totalVideoCompleteViews": 430, // Total number of times your video was watched at 95% of its length, including watches that skipped to this point. "totalVideoCompleteViewsAutoPlayed": 374, // Number of times your video started automatically playing and people watched it at 95% of its length, including watches that skipped to this point. "totalVideoCompleteViewsClickedToPlay": 56, // Number of times people clicked to play your video and watched it at 95% of its length, including watches that skipped to this point. "totalVideoCompleteViewsOrganic": 430, // Number of times your video was watches at 95% of its length without any paid promotion, including watches that skipped to this point. "totalVideoCompleteViewsOrganicUnique": 430, // The number of unique people who watched your video at 95% of its length without any paid promotion, including people that skipped to this point "totalVideoCompleteViewsPaid": 0, // Number of times your video was watched at 95% of its length after paid promotion, including watches that skipped to this point. "totalVideoCompleteViewsPaidUnique": 0, // The number of unique people who watched your video at 95% of its length after paid promotion, including people that skipped to this point. "totalVideoCompleteViewsUnique": 430, // Total number of unique people who watched your video at 95% of its length or more, including people that skipped to this point. // The totalVideoImpressions* family (Video Impressions) was retired by Meta (June 15, 2026); see the note at the top of this page. "totalVideoReactionsByTypeTotal": { // The total number of reactions to your post by type. "like": 13, "haha": 2 }, "totalVideoStoriesByActionType": { // Number of stories created about your Page Video, by action type. "like": 15 }, "totalVideoViewTimeByAgeBucketAndGender": { // Video view time (in ms) by age bucket and gender. "f1317": 23521, "m1317": 184052, "u1824": 2774, "f1824": 118193, "m1824": 1340281, "u2534": 11975, "f2534": 407006, "m2534": 3960750, "u3544": 27054, "f3544": 332576, "m3544": 5477331, "u4554": 41804, "f4554": 375449, "m4554": 3794202, "u5564": 35717, "f5564": 159041, "m5564": 3582574, "u65+": 8245, "f65+": 423739, "m65+": 2866201 }, "totalVideoViewTimeByDistributionType": { // Video view time (in ms) by distribution type. On asset level, there are three possible distribution types: (page_owned/shared/crossposted) "pageOwned": 23172485 }, "totalVideoViewTimeByRegionId": { // Video view time (in ms) by region ID. "californiaUnitedStates": 791788, "texasUnitedStates": 480891, "karnatakaIndia": 390297, "uttarPradeshIndia": 368698, "tamilNaduIndia": 359103, "queenslandAustralia": 295491, "newSouthWalesAustralia": 284222, "newYorkUnitedStates": 250035, "maharashtraIndia": 248465, "washingtonUnitedStates": 237430 }, "totalVideoViewTotalTime": 23172485, // Total time (in ms) video has been viewed. "totalVideoViewTotalTimeOrganic": 23172485, // Total time (in ms) video has been viewed in Feed or ticker or on your Page's Timeline. "totalVideoViewTotalTimePaid": 0, // Total time (in ms) video has been viewed after paid promotion. "totalVideoViews": 1283, "totalVideoViewsAutoplayed": 1237, // Number of times your video started automatically playing and people viewed it for 3 seconds or viewed it to the end, whichever came first. "totalVideoViewsByDistributionType": { // Video views by distribution type. On asset level, there are three possible distribution types: (page_owned/shared/crossposted) "pageOwned": 1283 }, "totalVideoViewsClickedToPlay": 46, // Number of times people clicked to play your video and viewed it for 3 seconds or viewed it to the end, whichever came first. "totalVideoViewsOrganic": 1283, // Number of times your video was viewed for 3 seconds or viewed to the end, whichever came first, without any paid promotion. "totalVideoViewsPaid": 0, // Number of times your video was viewed for 3 seconds or viewed to the end, whichever came first, after paid promotion. "totalVideoViewsSoundOn": 743, // Number of times your video sound was turned on and was viewed for 3 seconds or viewed to the end, whichever came first. "videoViews": 3 // Times your videos played for at least 3 seconds, or for nearly their total length if they're shorter than 3 seconds. During a single instance of a video playing, we'll exclude any time spent replaying the video. This includes live views. // videoViewsUnique was retired by Meta (June 15, 2026); see the note at the top of this page. }, "lastUpdated": "2022-04-23T18:44:29.778Z", "nextUpdate": "2022-04-23T19:19:29.778Z" }, "instagram": { "id": "17856916804532520", // Instagram Social Post ID "postUrl": "https://www.instagram.com/p/ChALNdaOT3G/", "analytics": { /* FEED Data */ "caption": "It always seems impossible until it's done. - Nelson Mandela", "commentsCount": 1, // Comments total "created": "2022-09-07T22:39:06Z", // Time post was created "engagementCount": 3, // Likes, comments, and saves "followsCount": 2, // Number of followers gained "likeCount": 1, // Likes total - organic likes, promoted post likes not included "mediaProductType": "FEED", // Type of IG Share: AD, FEED, STORY or REELS "mediaType": "IMAGE", // Media type: CAROUSEL_ALBUM, IMAGE, or VIDEO "mediaUrls": [ { "mediaUrl": "https://scontent.cdninstagram.com/v/t51.82787-15/516601117_17872451562389336_3472858781474899926_n.jpg?stp=dst-jpg_e35_tt6&_nc_cat=107&ccb=1-7&_nc_sid=18de74&_nc_ohc=-LOY7upopXoQ7kNvwEkqUMq&_nc_oc=AdmN47ZBlmciajpv7udoP1ku6J-FBgBGIrRskGgPmIOjT5D3WI9F21_Fg1Gi60-aI70&_nc_zt=23&_nc_ht=scontent.cdninstagram.com&edm=AEQ6tj4EAAAA&_nc_gid=PZY4V4Pvo8ePuq2CfSBp1A&oh=00_AfQplZLqrCEYwXkCutbmMAGIgpcBUkKqGcfRdDLZU4QHRA&oe=6872DFF9" } ], "profileActivityCount": 1,// The number of actions people take when they visit your profile after engaging with your post "profileVisitsCount": 11, // The number of times your profile was visited. "reachCount": 71, // Unique accounts that have seen the post "savedCount": 1, // Unique accounts have saved the post "sharesCount": 12, // Number of times the post was shared "username": "jackthegiant", "viewsCount": 71, // Total number of times the post has been seen. /* REELS Data */ "caption": "Gorgeous gifts for new Mum & Bub \n\n@mikimoocreations @romperandco @obdesigns @salusbody @enaproducts", "clipsReplaysCount": 21, // The number of times your reel starts to play again after an initial play of your reel. This is defined as replays of 1ms or more in the same reel session. "commentsCount": 1, // Comments total "created": "2022-09-15T06:58:55Z", "engagementCount": 183, // Number of likes, saves, comments, and shares on the reel, minus the number of unlikes, unsaves, and deleted comments. "igReelsAggregatedAllPlaysCount": 42, // The number of times your reel starts to play or replay after an impression is already counted. This is defined as plays of 1ms or more. Replays are counted after the initial play in the same reel session. Organic plays only; promoted post plays are not included. "igReelsAvgWatchTimeCount": 23, // The average amount of time spent playing the reel. This is calculated by the watch time divided by the number of plays. "igReelsVideoViewTotalTimeCount": 21, // The total amount of time the reel was played, including any time spent replaying the reel. "likeCount": 21, // Likes total on the reel "mediaProductType": "REELS", // Type of IG Share: AD, FEED, STORY or REELS "mediaType": "VIDEO", // Always "VIDEO" "mediaUrls": [ { "mediaUrl": "https://instagram.flis9-1.fna.fbcdn.net/o1/v...amug&oe=686ED4D3" } ], "playsCount": 680, // Number of times the reels starts to play after an impression is already counted. This is defined as video sessions with 1 ms or more of playback and excludes replays. Organic plays only; promoted post plays are not included. "reachCount": 689, // Number of unique accounts that have seen the reel at least once. Reach is different from impressions, which can include multiple views of a reel by the same account. "savedCount": 0, // Number of saves of the reel. "sharesCount": 2, // Number of shares of the reel. "username": "mordiallocflorist", "viewsCount": 40, // Total number of times the reel has been seen. /* STORY Data - Fewer than 5 insights will not show metric data. */ "commentsCount": 2, "created": "2024-09-16T08:37:57Z", "engagementCount": 10, "followsCount": 0, "likeCount": 3, "mediaProductType": "STORY", "mediaType": "IMAGE", "mediaUrls": [ { "mediaUrl": "https://scontent.cdninstagram.com/v/t51.82787-15/517935...we32" } ], "notEnoughViews": true, // True if fewer than 5 unique insights. If fewer than 5 unique insights, metric data not available. "navigationCount": 15, // Total nav interactions of taps and swipes "profileActivityCount": 3, "profileVisitsCount": 1, "reachCount": 10, "repliesCount": 3, "sharesCount": 2, "swipeForwardCount": 1, // Swipes "Next Story" on the native interface. "tapBackCount": 2, // Taps "Back" on the native interface. "tapForwardCount": 9, // Taps "Forward" on the native interface. "tapExitCount": 3, // Taps "Exit" on the native interface. "username": "ayrshare", "viewsCount": 10, // Total number of times the story has been seen. }, "lastUpdated": "2022-04-23T18:44:29.778Z", "nextUpdate": "2022-04-23T19:19:29.778Z" }, /* Corporate LinkedIn Account Example. Past 12 months, using a rolling 12-month window. */ "linkedin": { "id": "6783586276949000192", // LinkedIn Social Post ID "postUrl": "https://www.linkedin.com/feed/update/urn:li:share:6783586276949000192", "type": "corporate", // corporate or personal. Please see notes on personal analytics "analytics": { "clickCount": 0, // Clicks on a post - company only "commentCount": 0, // Comments count "comments": [ // Comment IDs - can be used to look up comments "urn:li:comment:(urn:li:activity:7226002855806021632,7226004971362627585)" ], "commentsState": "OPEN", // Comments allowed or not "engagement": 0, // Organic clicks, likes, comments, and shares over impressions - company only "impressionCount": 0, // Impressions on a post - company only "likeBy": [ "urn:li:person:Z_yXaxh_AB" // Look up the user with the brands endpoint ], "likeCount": 1, // Likes on a count "reactions": { // Get reactions on a LinkedIn share "like": 1, // "Like in the UI "praise": 2, // "Celebrate" in the UI "maybe": 3, // "Curious" in the UI "empathy": 3, // "Love" in the UI "interest": 2, // "Insightful" in the UI "appreciation": 5 // "Support" in the UI }, "shareCount": 0, // Shares on a post - company only "totalFirstLevelComments": 1, // COunt of the firt level comments "uniqueImpressionsCount": 0, // Unique impressions for a post - company only "videoViews": 1 // Number of times video viewed - company only }, "lastUpdated": "2022-04-23T18:44:29.778Z", "nextUpdate": "2022-04-23T19:19:29.778Z" }, /* Personal LinkedIn Account Example. Past 12 months, using a rolling 12-month window. */ "linkedin": { "id": "urn:li:share:7221186087895920444", "type": "personal", "analytics": { "commentCount": 12, // Comments count "comments": [ "urn:li:comment:(urn:li:activity:7221186088390799360,7224469537784479744)" ], "commentsState": "OPEN", "engagement": 0.07, // Organic clicks, likes, comments, and shares over impressions "impressionCount": 1543, // Impressions on the post "likeBy": [ { "from": { "name": "John Smith", "id": "qDyUWJx2s", "url": "https://www.linkedin.com/in/js", "description": "Business Analyst at Ayrshare" }, "media": { "id": "urn:li:image:C4D03AQG_8MpwM6tDzw", "mediaExpiresSeconds": 1728518400000, "url": "https://media.licdn.com/dms/image/C4D03AQG_8MpwM6tDzw" }, "platform": "linkedin", "profileImageUrl": "https://media.licdn.com/dms/image/C4D03AQG_8MpwM", "userName": "js", "originalUrn": "urn:li:like:(urn:li:person:qDyUWJxqIO,urn:li:activity:722118608839074444)", "type": "like" } ], "likeCount": 87, // Reaction count "reactions": { // Per-type reaction breakdown "like": 60, "praise": 15, "maybe": 0, "empathy": 12, "interest": 0, "appreciation": 0 }, "share": "urn:li:activity:7221186088390794444", "shareCount": 9, // Reshares of the post "totalFirstLevelComments": 1, "uniqueImpressionsCount": 1201, // Unique members reached (MEMBERS_REACHED) "videoViewers": 280, // Video posts only "videoViews": 320, // Video posts only "videoWatchTimeMs": 451000 // Video posts only }, "lastUpdated": "2024-08-04T22:46:15.660Z", "nextUpdate": "2024-08-04T22:57:15.660Z" }, "pinterest": { "id": "718464946813167315", // Pinterest Social Post ID "postUrl": "https://www.pinterest.com/pin/718464946813167315/", "analytics": { "altText": "Hello", "boardId": "480126078963936788", "boardOwner": { "username": "johnsmith" }, "boardSectionId": 12232, "createdAt": "2024-10-20T06:25:08", "creativeType": "REGULAR", "description": "The most amazing board", "dominantColor": "#4e6279", "hasBeenPromoted": false, "id": "480126010294119688", "impression": 26, "isOwner": true, "isStandard": true, "link": "https://www.mywebsite.com", "media": { "media_type": "image", "images": { "150x150": { "width": 150, "height": 150, "url": "https://i.pinimg.com/150x150/2b/86/64/2b.jpg" }, "400x300": { "width": 400, "height": 300, "url": "https://i.pinimg.com/400x300/2b/86/64/2b.jpg" }, "600x": { "width": 564, "height": 296, "url": "https://i.pinimg.com/564x/2b/86/64/2b.jpg" }, "1200x": { "width": 1200, "height": 630, "url": "https://i.pinimg.com/1200x/2b/86/64/2b.jpg" } } }, "note": "", "outboundClick": 4, "parentPinId": 23233, "pinClick": 2, "pinMetrics": null, "productTags": [], "profileVisit": 5, // Available for images only "quartile95PercentView": 4, "save": 1, "saveRate": 1, "title": "Looking great", "totalComments": 2, "totalReactions": 1, "userFollow": 1, // Available for images only /* Available for videos */ "video10sView": 3, "videoAvgWatchTime": 0, "videoMrcView": 0, "videoStart": 0, "videoV50WatchTime": 0 }, "lastUpdated": "2022-04-23T18:44:29.778Z", "nextUpdate": "2022-04-23T19:19:29.778Z" }, "reddit": { "id": "182zitr", "postUrl": "https://www.reddit.com/r/test/comments/182zitr/reddit_post_title/", "analytics": { "author": "ayrshare", "created": "2023-11-24T19:10:06.000Z", "permalink": "/r/test/comments/182zitr/reddit_post_title/", "subreddit": "test", "title": "Reddit Post Title", "ups": 2, "upvoteRatio": 1, "url": "https://www.reddit.com/r/test/comments/182zitr/reddit_post_title/" }, "lastUpdated": "2023-11-24T19:10:41.635Z", "nextUpdate": "2023-11-24T19:21:41.635Z" }, "snapchat": { "id": "490edf20-4068-5bab-bf65-a5882b1ezuec", "postUrl": "https://www.snapchat.com/add/chrisalpha1980/490edf20-4068-5bab-bf65-a5882b1ezuecc", "analytics": [ { "avgViewTime": 5230, // Average view time in milliseconds "completes": 187, // Number of times the post was played to completion "interactions": 42, // Number of times a user interacted with an asset. These include: tap forwards/backwards, swipe-away "mediaId": "490edf20-4068-5bab-bf65-a5882b1ezuec", "replies": 8, // Number of replies to this snap/story "shares": 12, // Number of times this post has been shared "snapCombinedUniques": 642, // Combined total of snapPaidUniques and storyUniques "snapCombinedViews": 829, // Combined total of snapPaidViews and views "snapPaidUniques": 174, // The sum of paid uniques for ads promoting the snaps within the Public Story "snapPaidViews": 203, // The sum of paid impression for ads promoting the snaps within the Public Story "storyAvgViewTime": 4870, // The calculation of the storyViewTime divided by storyUniques (milliseconds) "storyFavorites": 28, // Number of times the post has been liked "storySubscribes": 5, // Number of subscribe events occuring on the story post "storyUniques": 468, // Unique users who have viewed a story/snap within a story "storyViewTime": 2279160, // Milliseconds a story & snap in story asset has been viewed "storyViews": 626, // Views for Story container "swipeDowns": 15, // Number of swipe downs on this post "swipeUps": 23, // Number of swipe ups on this post "uniqueSessions": 512, // Number of unique sessions an asset has been engaged in "viewTime": 3264000, // Milliseconds this post has been viewed "viewers": 598, // Number of unique users who have viewed this post "views": 712 // Number of times an asset has been viewed } ], "lastUpdated": "2025-05-21T11:14:40.570Z", "nextUpdate": "2025-05-21T11:25:40.570Z" }, "threads": { "id": "17890643139123701", "postUrl": "https://www.threads.com/@ayrshare/post/DI4nmXrNQA1", "analytics": { "views": 2, "likes": 1, "replies": 1, "reposts": 0, "shares": 0, "quotes": 0 }, "lastUpdated": "2025-04-25T22:41:05.752Z", "nextUpdate": "2025-04-25T22:52:05.752Z" }, "tiktok": { "id": "7034682002927550598", // TikTok Social Post ID "postUrl": "https://www.tiktok.com/@borneild/video/7034682002927550598?utm_campaign=tt4d_open_api&utm_source=awawnhyictaos7o", "analytics": { "audienceCities": [ // Available 24-48 hours after posting. City distribution of video viewers for the top 10 cities. Must be an active post. See endpoint details. { "city_name": "US Teton County", "percentage": 3.1 }, { "city_name": "US Queens", "percentage": 6.3 } ], "audienceCountries": [ // Available 24-48 hours after posting. Country distribution of video viewers for the top 10 countries. Must be an active post. See endpoint details. { "country": "GB", "percentage": 0.0029 }, { "country": "US", "percentage": 0.9604 } ], "audienceGenders": [ // Updated by TikTok every 24-48 hours. { "percentage": 0.25, "gender": "Female" }, { "percentage": 0.5, "gender": "Male" }, { "percentage": 0.25, "gender": "Other" } ], "audienceTypes": [ { "percentage": 0, "type": "FOLLOWER_PERCENT" }, { "percentage": 0, "type": "NEW_VIEWER" }, { "percentage": 0, "type": "NON_FOLLOWER_PERCENT" }, { "percentage": 0, "type": "RETURN_VIEWER" } ], "averageTimeWatched": 5.6679, // Available 24-48 hours after posting. "commentsCount": 23, // Total number of lifetime comments. Available 24-48 hours after posting. "created": "2022-08-09T15:08:22Z", "embedUrl": "https://www.tiktok.com/embed/v2/7129893524253756713", "fullVideoWatchedRate": 0.0866, // Percentage of views that completed watching the full video. Available 24-48 hours after posting. "impressionSources": [ // Different sources for the impressions, ranked from the largest contribution to the smallest. Available 24-48 hours after posting. { "impression_source": "Search", "percentage": 0.0217 }, { "impression_source": "Sound", "percentage": 0 }, { "impression_source": "Follow", "percentage": 0 }, { "impression_source": "For You", "percentage": 0.917 }, { "impression_source": "Hashtag", "percentage": 0 }, { "impression_source": "Personal Profile", "percentage": 0 } ], "likeCount": 22, // Total number of lifetime likes. Available 24-48 hours after posting. "mediaType": "video", "musicTitle": "♬ original sound - tiff",// If available "musicUrl": "https://www.tiktok.com/music/original-sound-6689804660171082501?refer=embed", // If available "name": "Mackly", // If available "post": "Scramble up ur name & I'll try to guess it😍❤️ #foryoupage #petsoftiktok #aesthetic", "postUrl": "https://www.tiktok.com/@tiktoktime/video/7129893524253756713?utm_campaign=tt4d_open_api&utm_source=awawnhyictaos7o7", "reach": 252, // The number of people who watched your published content at least once. Available 24-48 hours after posting. "shareCount": 1, // Total number of lifetime shares. Available 24-48 hours after posting. "tags": [ // Tags included in the description { "tag": "#foryoupage", "url": "https://www.tiktok.com/tag/foryoupage" }, { "tag": "#petsoftiktok", "url": "https://www.tiktok.com/tag/petsoftiktok" }, { "tag": "#aesthetic", "url": "https://www.tiktok.com/tag/aesthetic" } ], "thumbnailHeight": 576, // If available "thumbnailUrl": "https://p16-sign.tiktokcdn-us.com/obj/tos-useast5-p-0068-tx", "thumbnailWidth": 1006, // If available "url": "https://www.tiktok.com/@tiktoktime", "videoDuration": 13.984, // Video duration in seconds. "videoViewRetention": [ // This metric indicates how many of your viewers are still watching after a certain amount of time. { "percentage": 0.1, "second": "155" }, { "percentage": 0.07, "second": "234" } ], /* Total number of lifetime users who viewed the video. Available 24-48 hours after posting. If the user swipes away from the ad then swipes back, it would be counted as 2 impressions. Therefore, a new video view will be counted again with the new impression session. */ "videoViews": 34 }, "lastUpdated": "2022-04-23T18:44:29.778Z", "nextUpdate": "2022-04-23T19:19:29.778Z" }, /* Note: if the post was sent as a Twitter Thread, the return will be an array, "twitter": [] */ "twitter": { "id": "1313589441919827982", // Twitter Social Post ID "postUrl": "https://www.twitter.com/myaccount/1313589441919827982", "analytics": { "created": "2022-09-07T22:12:10.000Z", "entities": { "urls": [ { "start": 77, // Starting character position (inclusive) "end": 96, // Ending character position (exclusive) "url": "https://t.co/abc123", // Shortened t.co URL "expandedUrl": "https://www.ayrshare.com/docs", // Full URL after redirect "displayUrl": "ayrshare.com/docs", // User-friendly display version "unwoundUrl": "https://www.ayrshare.com/docs" // Final destination URL } ], "hashtags": [ { "start": 97, // Starting character position (inclusive) "end": 112, // Ending character position (exclusive) "tag": "SocialMediaAPI" // Hashtag text without # } ], "mentions": [ { "start": 113, // Starting character position (inclusive) "end": 122, // Ending character position (exclusive) "username": "ayrshare" // Username without @ } ], "cashtags": [ { "start": 123, // Starting character position (inclusive) "end": 128, // Ending character position (exclusive) "tag": "META" // Stock symbol without $ } ], "annotations": [ { "start": 46, // Starting character position (inclusive) "end": 54, // Ending character position (exclusive) "probability": 0.9456, // Confidence score (0.0 to 1.0) "type": "Product", // Entity type: Person, Place, Product, Organization, Other "normalizedText": "Ayrshare" // Standardized entity name } ] }, "media": [ { "width": 1920, "durationMs": 28240, "height": 1080, "mediaKey": "7_1739849636389814272", "previewImageUrl": "https://pbs.twimg.com/ext_tw_video_thumb/1739849636389814272/pu/img/bCkAdkD0R00-ZlkY.jpg", "type": "video", "mediaUrls": [ // available for videos and gifs { "bitRate": 256000, "contentType": "video/mp4", "mediaUrl": "https://video.twimg.com/ext_tw_video/1739849636389814272/pu/vid/avc1/480x270/4RoZCqynednVMFgy.mp4?tag=12" }, { "bitRate": 832000, "contentType": "video/mp4", "mediaUrl": "https://video.twimg.com/ext_tw_video/1739849636389814272/pu/vid/avc1/640x360/RYy5iHTIxu7_iyZm.mp4?tag=12" }, { "bitRate": 2176000, "contentType": "video/mp4", "mediaUrl": "https://video.twimg.com/ext_tw_video/1739849636389814272/pu/vid/avc1/1280x720/9UpR90ekFA-9Ucxo.mp4?tag=12" }, { "contentType": "application/x-mpegURL", "mediaUrl": "https://video.twimg.com/ext_tw_video/1739849636389814272/pu/pl/0AVy2xIOLQcsrEZG.m3u8?tag=12&container=fmp4" } ] } ], "name": "Wonder World", "post": "Just launched our social media campaign using Ayrshare! Check out the API at https://t.co/abc123 #SocialMediaAPI @ayrshare $META", "publicMetrics": { // Organic and paid metrics available publicly. Use this to measure Tweet engagement. "retweetCount": 0, // Retweets count "quoteCount": 0, // Times the Tweet was quoted count "likeCount": 0, // Likes for the Tweet count "replyCount": 0, // Replies count "quoteCount": 0, // Times the Tweet was quoted count "bookmarkCount": 0, // Bookmarks count "impressionCount": 23 // How many times the Tweet has been viewed (not unique by user). A view is counted if any part of the Tweet is visible on the screen. }, "nonPublicMetrics": { // Organic and paid metrics not available publicly. Not available for Tweets older than 30 days. Use this to determine the total number of impressions generated for the Tweet. "userProfileClicks": 1, // User profile clicks "engagements": 1, // How many times the Tweet has been engaged with. "impressionCount": 5, // How many times the Tweet has been viewed (not unique by user). A view is counted if any part of the Tweet is visible on the screen. "video": { // Users who played through to each quartile in a video. This reflects the number of quartile views across all Tweets in which the given video has been posted. "playback25Count": 4, "playback50Count": 2, "playback75Count": 1, "playback0Count": 4, "playback100Count": 1 } }, "organicMetrics": { // Non-paid metrics. Not available for Tweets older than 30 days. Use this to measure organic engagement for the Tweet. "likeCount": 0, // Tweet likes "impressionCount": 5, // How many times the Tweet has been viewed (not unique by user). A view is counted if any part of the Tweet is visible on the screen. "replyCount": 0, // Replies count "retweetCount": 0, // R countetweets "userProfileClicks": 1, // User profile clicks "video": { // Users who played through to each quartile in a video. This reflects the number of quartile views across all Tweets in which the given video has been posted. "playback25Count": 4, "playback0Count": 4, "playback100Count": 1, "viewCount": 4, "playback50Count": 2, "playback75Count": 1 } }, "poll": { // Present if Tweet has a poll "durationMinutes": 5, // Duration in minutes "endDatetime": "2023-05-29T17:28:05.000Z", "votingStatus": "open", // "open" or "closed" "id": "1663234520718371960", "options": [ { "position": 1, "label": "yes", "votes": 3 }, { "position": 2, "label": "maybe", "votes": 1 }, { "position": 3, "label": "no", "votes": 0 } ] }, "urls": [ // Available for long Tweets only { "start": 866, "end": 889, "url": "https://t.co/3r6xz4hBnM", "expandedUrl": "https://www.cnn.com", "displayUrl": "cnn.com" } ], "username": "wondrous" }, "lastUpdated": "2022-04-23T18:44:29.778Z", "nextUpdate": "2022-04-23T19:19:29.778Z" }, "youtube": { "id": "RWJMpOUGicU", // YouTube Social Post ID "postUrl": "https://youtu.be/RWJMpOUGicU", "analytics": { "averageViewDuration": 6, // The average length, in seconds, of video playbacks. "averageViewPercentage": 92.01, // The average percentage of a video watched during a video playback. "channelTitle": "youtubeHandle", "comments": 22, "created": "2022-08-15T19:14:44Z", "description": "Doubt is not a pleasant condition, but certainty is absurd. - Voltaire", "dislikes": 3, "engagedViews": 148, // The number of views counted past the initial seconds of playback — YouTube's view-counting methodology prior to August 24, 2026. "estimatedMinutesWatched": 23, // The number of minutes that users watched videos for the specified channel, content owner, video, or playlist. "likes": 54, "liveBroadcastDetails": { // Only present if liveBroadcast is "live" or "upcoming" "activeLiveChatId": "Cg0KC3Ez", // The ID of the currently active live chat attached to this video. Not present if broadcast complete and chat no longer live "actualStartTime": "2023-05-04T10:14:56Z", // The time that the broadcast actually started. "actualEndTime": "2023-05-04T10:15:15Z", // The time that the broadcast actually ended. "concurrentViewers": 384343, // The number of viewers currently watching the broadcast. Not present if the broadcast has ended. "scheduledEndTime": "2023-05-04T10:15:00Z", // The time that the broadcast is scheduled to end if a scheduled end time was set. "scheduledStartTime": "2023-05-04T10:15:00Z" // The time that the broadcast is scheduled to begin. }, "liveBroadcast": "live", // Values: "none", "live", "upcoming" "madeForKids": false, "privacyStatus": "public", "publishedAt": "2023-05-08T12:30:26Z", "subscribersGained": 23, // The number of times that users subscribed to a channel. "subscribersLost": 1, // The number of times that users unsubscribed from a channel. "tags": ["sweet", "sour"], // Tags assigned to video "thumbnailUrl": "https://i.ytimg.com/vi/RWJMpOUGicU/default.jpg", "title": "Yo time", "videosAddedToPlaylists": 2, // The number of times that videos were added to any YouTube playlists. The videos could have been added to the video owner's playlist or to other channels' playlists. "views": 153 }, "lastUpdated": "2022-04-23T18:44:29.778Z", "nextUpdate": "2022-04-23T19:19:29.778Z" }, "status": "success", "code": 200, "id": "IHvCLacgPc6hMU9IQ6oK" } ``` ```json 200: Backfilled Response theme={"system"} { "facebook": { "id": "104923907983682_108329000309742", "postUrl": "https://www.facebook.com/104923907983682_108329000309742", "analytics": { "commentsCount": 5, "likeCount": 23, "sharesCount": 12, "mediaView": 450, "backfilledFrom": "2026-04-08T14:30:00.000Z" }, "lastUpdated": "2026-04-09T10:15:00.000Z", "nextUpdate": "2026-04-09T10:26:00.000Z" }, "status": "success", "code": 200, "id": "IHvCLacgPc6hMU9IQ6oK" } ``` ```json 200: Recovered Response theme={"system"} { "facebook": { "id": "104923907983682_108329000309742", "postUrl": "https://www.facebook.com/104923907983682_108329000309742", "analytics": { "commentsCount": 5, "likeCount": 23, "sharesCount": 12, "mediaView": 450, "recoveredFrom": "2026-04-08T14:30:00.000Z" }, "lastUpdated": "2026-04-09T10:15:00.000Z", "nextUpdate": "2026-04-09T10:26:00.000Z" }, "status": "success", "code": 200, "id": "IHvCLacgPc6hMU9IQ6oK" } ``` ```json 404 - Bad Request theme={"system"} /* Often if manually deleted at social network */ { "status": "error", "code": 116, "id": "N7GAZeJSAYcdCpHSm3xQ", // Ayrshare Post ID "facebook": { "action": "post", "status": "error", "code": 186, "message": "Post ID not found. Please verify the top level ID returned from the /post endpoint is being sent and the post at the social network has not been deleted.", "id": "104619420979033_588326339980968" // Facebook Platform ID }, "instagram": { "action": "post", "status": "error", "code": 186, "message": "Post ID not found. Please verify the top level ID returned from the /post endpoint is being sent and the post at the social network has not been deleted.", "id": "18349092271045074" // Instagram Platform ID }, "linkedin": { "action": "post", "status": "error", "code": 186, "message": "Post ID not found. Please verify the top level ID returned from the /post endpoint is being sent and the post at the social network has not been deleted.", "id": "urn:li:share:7038277251594878976" // LinkedIn Platform ID }, "pinterest": { "action": "post", "status": "error", "code": 186, "message": "Post ID not found. Please verify the top level ID returned from the /post endpoint is being sent and the post at the social network has not been deleted. Pin not found." }, "twitter": { "action": "post", "status": "error", "code": 186, "message": "Could not find tweet with id: [1632511562660433929].", "id": "1632511562660433929" // Twitter Platform ID }, "youtube": { "action": "post", "status": "error", "code": 186, "message": "Post ID not found. Please verify the top level ID returned from the /post endpoint is being sent and the post at the social network has not been deleted.", "id": "rAMFoqZeOGg" // YouTube Platform ID }, "lastUpdated": "2023-03-06T21:59:06.064Z", "nextUpdate": "2023-03-06T22:10:06.064Z" } ``` ```json 404 - Not Found theme={"system"} { "action": "analytics", "status": "error", "code": 186, "message": "Post ID not found.", "id": "7aVBe0jw27DNwFq92KZ" } ``` # Analytics on a Social Network Source: https://www.ayrshare.com/docs/apis/analytics/social POST /analytics/social Get analytics and demographics on a user's social profile, such as impressions, views, and followers > [!NOTE] > > **Targeting X/Twitter?** Starting March 31, 2026, all X operations require your own API credentials. After linking X via OAuth, include these 2 headers in your request: > > `X-Twitter-OAuth1-Api-Key` — Your API Key (Consumer Key) > > `X-Twitter-OAuth1-Api-Secret` — Your API Key Secret (Consumer Secret) > > **One-time setup per Ayrshare account.** You create one X Developer App and reuse the same API Key and Secret across every sub-profile / end-user you link. You do *not* create a new app per customer. > > Not linked yet? See the [full setup guide](/docs/dashboard/connect-social-accounts/x-twitter-byo-keys) to connect your X account. > > Your keys are never logged or stored by Ayrshare. Get analytics and demographics on a user's social profile, such as impressions, views, and followers. Currently available for Bluesky, Facebook Pages, Google My Business, Instagram, LinkedIn, Pinterest, Reddit, Snapchat, Threads, TikTok, X/Twitter, and YouTube. > [!WARNING] > > **Facebook: some reach and video metrics were retired by Meta (June 15, 2026).** Meta removed the unique impression and 3-second video-view (unique) Insights metrics across all Graph API versions, so the Facebook `analytics` object no longer returns the `pagePostsImpressions*` family (`pagePostsImpressions`, `pagePostsImpressionsPaid`, `pagePostsImpressionsUnique`, `pagePostsImpressionsOrganicUnique`, `pagePostsImpressionsViral*`, `pagePostsImpressionsNonviral*`, `pagePostsServedImpressionsOrganicUnique`) or `pageVideoViewsUnique`. For reach, use `pageTotalMediaViewUnique`, Meta's Total Unique Media Views replacement for the retired unique-impression fields. Meta reports it per day, and the value is those daily counts added together, so a person who viewed your content on two different days counts twice. `pageMediaView` remains available for total, non-unique views. `pagePostEngagements`, `pageVideoViews`, and `pageVideoViewsPaid` are unaffected. Reference: [Upcoming API Changes — June 15, 2026](/docs/whatsnew/upcoming-api-changes#june-15-2026).
    • Facebook Page analytics is only available on Pages with 100 or more likes, such as demographics. Facebook typically updates their metrics once every 24 hours.
    • Instagram may take up to 48 hours to calculate the analytics data. Follower count analytics is not available with fewer than 100 followers. Demographic metrics only return the top 45 performers, only viewers for whom we have demographic data are used in demographic metric calculations, and demographic data is not returned if the Instagram User has fewer than 100 engagements during the last 30 days.
    • When retrieving Instagram social analytics, demographic information may not appear in the response for particular metrics. Demographic information will appear when the metrics have more than 100 people in each breakdown. Please see [Instagram Analytics Demographics Warning](/docs/help-center/technical-support/instagram-analytics-demographics-warning) for more information.
    • LinkedIn supports both Company Page analytics and personal (member) profile analytics. For personal profiles, the `analytics` object includes a lifetime `followersCount`, daily follower growth in `followersDaily[]`, and aggregate post metrics (`impressionCount`, `uniqueImpressionsCount`, `likeCount`, `commentCount`, `shareCount`). LinkedIn `count` totals are eventually consistent, but not immediately consistent, and can take up to 24-48 hours in some cases. The aggregate `shareCount`, `likeCount`, and `commentCount` for personal profiles are best-effort and may differ slightly from the numbers shown in the LinkedIn UI.
    • TikTok can take 24-48 hours to update their analytics data, such as video views, demographics, likes, shares, and comments.
    • Please see the [post analytics endpoint](/docs/apis/analytics/post) for additional info.
    ## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` **Profile-Key** `string` [Profile Key](/docs/apis/overview#profile-key-format) of a User Profile. Format: `Profile-Key: PROFILE_KEY` ## Body Parameters **platforms** `array` *required* Social media platforms to get analytics. Accepts an array of strings with values: ```json theme={"system"} { "platforms": ["bluesky", "instagram", "facebook", "gmb", "linkedin", "pinterest", "reddit", "snapchat", "threads", "tiktok", "twitter", "youtube"] } ``` **quarters** `integer` Specifies how many quarters of historical data to return. A quarter is: * 85 days for Facebook * 90 days for Google Business Profile, Instagram, TikTok, and YouTube * 90 days for Snapchat (capped at 1 quarter / 90 days max due to Snapchat API limitations) Available for Facebook, Google Business Profile, Instagram, Snapchat, TikTok, and YouTube platforms. Valid values: 1–4. Only values greater than 0 activate date filtering. **Date filtering (Google Business Profile, Instagram & TikTok):** Date filtering is active when `daily=true` OR `quarters > 0`. If neither `daily` nor `quarters` is provided, all-time data is returned with no date filter applied (for Google Business Profile, all-time means the trailing 24 months). Note: `quarters: 0` is now treated as no date range (all-time data). Previously, `quarters: 0` was treated as `quarters: 1`. **daily** `boolean` (default: false) When set to `true`, returns analytics data as daily time-series values instead of aggregated totals. This option is only available for Facebook, Google Business Profile, Instagram, Snapchat, TikTok, and YouTube platforms. Due to the larger data size, using [compression](/docs/apis/overview#compression) is recommended. For Google Business Profile, Instagram, and TikTok, setting `daily=true` also activates date filtering using a default shorter quarters window. **Instagram reach:** With `daily=true`, the Instagram response returns a nested `reach` object (containing `period` and a `values` time-series) instead of the scalar `reachCount` field that is returned in non-daily mode. **period60Days** `boolean` (default: true) For TikTok analytics, when set to true, this returns only the 60-day aggregate totals for comments, shares, and views (`commentCountTotal`, `shareCountTotal`, `viewCountTotal`). This provides faster response times compared to retrieving the full analytics history. Set to `false` to get the full analytics history. Note: Do not use this parameter together with `daily=true` as they are incompatible. Important: As of March 1, 2025, TikTok moved to 60-day totals. As of April 2026, TikTok uses quarters-based date filtering — use the `quarters` parameter to control the date window (e.g., `quarters: 1` = 90 days, `quarters: 2` = 180 days). See [upcoming changes](/docs/whatsnew/upcoming-api-changes#march-1-2025) for details. **youtube** `object` Platform-specific options for YouTube analytics. **`lifetime`** (boolean, default: `false`): When set to `true`, includes `lifetimeLikes` in the response - the sum of likes across all public videos on the channel. This is computed by fetching all videos and summing their like counts, so it may take longer for channels with many videos. **Threshold:** Channels with more than 1,000 videos will return `lifetimeLikes: null` and a warning in the top-level [`warnings`](#warnings) array. This prevents excessive API usage. **Caching:** Per-channel, with shorter TTLs for non-success outcomes so retries pick up state changes promptly: * Successful `lifetimeLikes` value: **24 hours**. * 1,000-video bailout (warning `code: 445`): **1 hour** — short enough that a channel which deletes videos to drop below the threshold doesn't have to wait a full day for a real value. * Transient YouTube Data API failures (warning `code: 446`): **not cached** — the next request retries. **Note:** Deleted or private videos are excluded from the sum, so the total may differ from the "true" lifetime likes for channels that have removed videos. **Request example:** ```json theme={"system"} { "platforms": ["youtube"], "youtube": { "lifetime": true } } ``` **userId** `string` X/Twitter only. This parameter allows you to retrieve posts from a specific X/Twitter user by their numeric ID, rather than from your linked account. For example, to get all posts from the handle `@Google`, you would use their numeric userId `20536157`. You can find any X/Twitter user's numeric userId by using the [Brands Get User](/docs/apis/listen/brand-user) endpoint. Note: Use only the API KEY in the header to make this request. Do not include the Profile Key. **userName** `string` X/Twitter only. This parameter allows you to retrieve posts from a specific X/Twitter user by their handle, rather than from your linked account. For example, to get all posts from the handle `@Google`. Note: Use only the API KEY in the header to make this request. Do not include the Profile Key. ```bash cURL theme={"system"} curl \ -H "Authorization: Bearer API_KEY" \ -H 'Content-Type: application/json' \ -d '{"platforms": ["bluesky", "facebook", "gmb", "instagram", "linkedin", "pinterest", "reddit", "snapchat", "threads", "tiktok", "twitter", "youtube"], "quarters": 1}' \ -X POST https://api.ayrshare.com/api/analytics/social ``` ```javascript JavaScript theme={"system"} const API_KEY = "API_KEY"; fetch("https://api.ayrshare.com/api/analytics/social", { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${API_KEY}` }, body: JSON.stringify({ platforms: [ "bluesky", "facebook", "gmb", "instagram", "linkedin", "pinterest", "reddit", "snapchat", "threads", "tiktok", "twitter", "youtube" ], quarters: 1 }) }) .then((res) => res.json()) .then((json) => console.log(json)) .catch(console.error); ``` ```python Python theme={"system"} import requests payload = {'platforms': ['bluesky', 'facebook', 'gmb', 'instagram', 'linkedin', 'pinterest', 'reddit', 'snapchat', 'threads', 'tiktok', 'twitter', 'youtube']} headers = {'Content-Type': 'application/json', 'Authorization': 'Bearer API_KEY'} r = requests.post('https://api.ayrshare.com/api/analytics/social', json=payload, headers=headers) print(r.json()) ``` ```php PHP theme={"system"} ['bluesky', 'facebook', 'gmb', 'instagram', 'linkedin', 'pinterest', 'reddit', 'snapchat', 'threads', 'tiktok', 'twitter', 'youtube'] // required ]); $curl = curl_init($apiUrl); curl_setopt_array($curl, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_HTTPHEADER => $headers, CURLOPT_POSTFIELDS => $data ]); $response = curl_exec($curl); if ($response === false) { echo 'Curl error: ' . curl_error($curl); } else { echo json_encode(json_decode($response), JSON_PRETTY_PRINT); } curl_close($curl); ``` ```go Go theme={"system"} package main import ( "bytes" "encoding/json" "log" "net/http" ) func main() { message := map[string]interface{}{ "platforms": []string{"bluesky", "facebook", "gmb", "instagram", "linkedin", "pinterest", "reddit", "snapchat", "threads", "tiktok", "twitter", "youtube"}, } bytesRepresentation, err := json.Marshal(message) if err != nil { log.Fatalln(err) } req, _ := http.NewRequest("POST", "https://api.ayrshare.com/api/social", bytes.NewBuffer(bytesRepresentation)) req.Header.Add("Content-Type", "application/json; charset=UTF-8") req.Header.Add("Authorization", "Bearer API_KEY") res, err := http.DefaultClient.Do(req) if err != nil { log.Fatal("Error:", err) } res.Body.Close() } ``` ```csharp C# theme={"system"} using System; using System.Net.Http; using System.Text; using System.Threading.Tasks; namespace SocialPOSTRequest_csharp { class Social { private static readonly HttpClient client = new HttpClient(); static async Task Main(string[] args) { string API_KEY = "API_KEY"; string url = "https://api.ayrshare.com/api/analytics/social"; client.DefaultRequestHeaders.Add("Authorization", "Bearer " + API_KEY); string json = "{\"platforms\": [\"bluesky\", \"facebook\", \"gmb\", \"instagram\", " + "\"linkedin\", \"pinterest\", \"reddit\", \"snapchat\", \"threads\", \"tiktok\", \"twitter\", \"youtube\"]}"; try { var content = new StringContent(json, Encoding.UTF8, "application/json"); HttpResponseMessage response = await client.PostAsync(url, content); response.EnsureSuccessStatusCode(); string responseBody = await response.Content.ReadAsStringAsync(); Console.WriteLine(responseBody); } catch (HttpRequestException e) { Console.WriteLine($"Error: {e.Message}"); } } } } ``` > [!NOTE] > > When cumulative metrics (e.g., followers, likes) are temporarily unavailable from the social network, the API automatically backfills them from stored data. Two optional fields may appear in the per-platform `analytics` object: > > * **`backfilledFrom`** (string, ISO 8601) — Present when one or more cumulative metrics were substituted from stored data. The timestamp indicates when the stored data was last updated. > * **`recoveredFrom`** (string, ISO 8601) — Present when the entire analytics response was recovered from stored data due to a complete API failure. The timestamp indicates when the stored data was last updated. For Snapchat, where `analytics` is a list, `recoveredFrom` appears beside `analytics` instead. > > Stored data older than 4 days is considered stale and will not be used for backfill or recovery. > > **LinkedIn `reactions`:** The post-level cumulative `reactions` metric (an object of per-type reaction counts) is subject to this empty-only backfill. When LinkedIn rate-limits the reactions fetch, the value carries forward from the last successful snapshot — and `backfilledFrom` is set to that snapshot's timestamp — instead of regressing to empty. > [!NOTE] > > **LinkedIn personal (member) analytics — re-link required.** Personal LinkedIn profiles linked before member analytics shipped do not have the required analytics scopes. Social analytics requests for those profiles return [error code `475`](/docs/errors/errors-ayrshare#linkedin-analytics-errors) ("re-link your LinkedIn profile to enable analytics"). The account owner must re-link their LinkedIn profile on the Social Accounts page to grant the new scopes. Allow a few minutes after re-linking for code `475` to clear (Ayrshare and LinkedIn both briefly cache the permission state, typically \~5-10 minutes). Posting is unaffected. > [!NOTE] > > **`warnings`** (array of objects, optional top-level field) — Present only when Ayrshare needs to inform the caller of a non-fatal condition (e.g., an opt-in computation was skipped). Absent from the response when there is nothing to warn about. > > Each entry is a structured object, not a free-form string: > > | Field | Type | Description | > | - | - | - | > | `action` | string | The operation context that produced the warning (e.g. `"analytics"`). | > | `status` | string | Always `"warning"` for entries in the `warnings` array. | > | `code` | number | Stable Ayrshare warning code. Safe to match on in client code. | > | `message` | string | Human-readable description. | > | `details` | string | Optional additional context specific to this warning instance. | > > Known warning codes: > > * `445` — `lifetimeLikes` skipped because the YouTube channel exceeds the 1,000-video threshold. > * `446` — `lifetimeLikes` unavailable because the YouTube Data API returned errors for the uploads playlist or every `videos.list` batch. ```json 200: Success Aggregate {3, 29, 112, 129, 194, 256, 277, 355, 392, 461, 480, 558, 598} theme={"system"} { "status": "success", "bluesky": { "analytics": { "associated": { // Group of associated features and capabilities "lists": 0, // Number of lists created by the user "feedgens": 0, // Number of custom feeds generated by the user "starterPacks": 0, // Number of starter packs created by the user "labeler": false // Whether the user can create content labels }, "avatar": "https://cdn.bsky.app/img/avatar/plain/did:plc:n7atr", // URL to user's profile picture "created": "2024-12-14T20:39:21.343Z", // Timestamp when the account was created "displayName": "Jumping Jack", // User's chosen display name "followersCount": 15, // Number of users following this account "followsCount": 5, // Number of users this account follows "handle": "jack.bsky.social", // User's unique handle/username "id": "did:plc:n7atrjd22xgkmgwig6dzlh", // Bluesky Social ID "indexedAt": "2024-12-14T20:39:21.343Z", // Timestamp when this data was last indexed "labels": [], // Array of content labels applied to the account "postsCount": 20, // Number of posts made by the user "viewer": { // Information about the viewing user's relationship "muted": false, // Whether the viewing user has muted this account "blockedBy": false // Whether this account has blocked the viewing user } }, "lastUpdated": "2025-01-11T20:05:05.214Z", "nextUpdate": "2025-01-11T20:16:05.214Z" }, "facebook": { /* See specific time ranges - Lifetime unless otherwise noted */ "analytics": { "about": "The best site to watch movies online.", "birthday": "11/20/1985", // Applicable to Pages representing people "caterory": "Movie", // The Page's category. e.g. Product/Service, Computers/Technology. "emails": ["john@example.com"], "engagement": { // If available, otherwise use fanCount "count": 587, // Page like count, same as fanCount "socialSentence": "587 people like this." }, "fanCount": 587, "followersCount": 587, "id": "102619320979033", "instagramBusinessAccount": { "id": "17841452212707498" // Present if an Instagram account is linked with the Facebook page }, "isPublished": true, // If the FB Page is publicly visable. "link": "https://www.facebook.com/102619320979033", "location": { // If a location is set "street": "142 W 57th St", "zip": "10019" }, "name": "theGoodone", "pageFollows": 2, "pageMediaView": 929, // The number of times your content was played or displayed. Content includes videos, posts, stories and ads. "pageMediaViewIsFromAds": 1, // The number of times your content was played or displayed from ads. "pageMediaViewIsFromFollowers": 18, // The number of times your content was played or displayed from followers. "pagePostEngagements": 20, // The number of interactions with your posts such as reactions, comments, shares and more. // The pagePostsImpressions* family and pagePostsServedImpressionsOrganicUnique were retired by Meta (June 15, 2026); see the note at the top of this page. "pageTotalMediaViewUnique": 412, // Reach: unique people your content was played or displayed to, counted per day and added up across the range. // --- Page Video Data --- "pageVideoCompleteViews30s": 32, // The number of times your Page's videos played for at least 30 seconds, or for nearly their total length if they're shorter than 30 seconds. During a single instance of a video playing, we'll exclude any time spent replaying the video. "pageVideoCompleteViews30sAutoplayed": 19, // Autoplayed 30s complete. "pageVideoCompleteViews30sClickToPlay": 5, // Click to play 30s complete. "pageVideoCompleteViews30sOrganic": 23, // Organic 30s complete. "pageVideoCompleteViews30sPaid": 2, // Paid 30s complete. "pageVideoCompleteViews30sRepeatViews": 3, // Repeat 30s complete. "pageVideoCompleteViews30sUnique": 3, // Unique 30s complete. "pageVideoRepeatViews": 4, // The number times of repeat views. "pageVideoViews": 31, // The number of times your Page's videos played for at least 3 seconds, or for nearly their total length if they're shorter than 3 seconds. During a single instance of a video playing, we'll exclude any time spent replaying the video. "pageVideoViewsAutoplayed": 115, // Number of views that autoplayed on a user's screen "pageVideoViewsOrganic": 32, // The number of organic video views. "pageVideoViewsPaid": 6, // The number of times your Page's promoted videos played for at least 3 seconds, or for nearly their total length if they're shorter than 3 seconds. For each impression of a video, we'll count video views separately and exclude any time spent replaying the video. "pageVideoViewsByPaidNonPaid": { // Breakdown of view views by paid and non-paid "total": 32, "unpaid": 5, "paid": 27 }, "pageVideoViewsByUploadedHosted": { // Daily video views on a page-level broken down by all variants of page-uploaded and page-hosted variants. "pageUploaded": 147, "pageUploadedFromCrossposts": 0, "pageUploadedFromShares": 3, "pageHostedCrosspost": 0, "pageHostedShare": 0, "pageOwned": 144 }, "pageVideoViewsClickToPlay": 32, // The number of times your Page's videos played for at least 3 seconds, or for nearly their total length if they're shorter than 3 seconds, after people clicked play. During a single instance of a video playing, we'll exclude any time spent replaying the video. "pageVideoViewsOrganic": 147, // The number of times your Page's videos played for at least 3 seconds, or for nearly their total length if they're shorter than 3 seconds, by organic reach. During a single instance of a video playing, we'll exclude any time spent replaying the video. "pageVideoViewsPaid": 0, // The number of times your Page's promoted videos played for at least 3 seconds, or for nearly their total length if they're shorter than 3 seconds. For each impression of a video, we'll count video views separately and exclude any time spent replaying the video. // pageVideoViewsUnique was retired by Meta (June 15, 2026); see the note at the top of this page. "reactions": { // Total over past 180 days "like": 1, // Like reactions - The "like" reaction counts include both "like" and "care" reactions. "love": 1, // Love reactions "anger": 1, // Anger reactions "haha": 1, // Haha reactions "wow": 1, // Wow reactions "sorry": 1, // Sorry reactions "total": 6 // Total number of reactions }, "unreadMessageCount": 12, // Total unread FB Messages - if applicable "username": "ayrshare", "verified": true, // verified Facebook Page "website": "https://www.theGoodone.com" }, "quarters": 4, "lastUpdated": "2024-04-25T21:28:10.877Z", "nextUpdate": "2024-04-25T21:39:10.877Z" }, /* * Google Business Profile analytics totals are for the past 24 months unless a date * range is active, which happens when `daily=true` or `quarters` is greater than 0. * (`quarters: 0` is not a range — it returns the same 24-month totals.) With a range * active the window is `quarters` × 90 days, and `daily=true` without a `quarters` * greater than 0 uses one quarter. `quarters` is then returned alongside `analytics` * (see the Daily example). */ "gmb": { "analytics": { "businessBookings": 0, // The number of bookings received from the business profile. "businessConversations": 0, // The number of message conversations received on the business profile. "businessDirectionRequests": 328, // The number of times a direction request was requested to the business location. "businessFoodOrders": 0, // The number of food orders received from the business profile. "businessImpressionsDesktopMaps": 153, // Business impressions on Google Maps on Desktop devices. Multiple impressions by a unique user within a single day are counted as a single impression. "businessImpressionsDesktopSearch": 9675, // Business impressions on Google Search on Desktop devices. Multiple impressions by a unique user within a single day are counted as a single impression. "businessImpressionsMobileMaps": 65, // Business impressions on Google Maps on Mobile devices. Multiple impressions by a unique user within a single day are counted as a single impression. "businessImpressionsMobileSearch": 2070, // Business impressions on Google Search on Mobile devices. Multiple impressions by a unique user within a single day are counted as a single impression. "callClicks": 2, // The number of times the business profile call button was clicked. "websiteClicks": 134 // The number of times the business profile website was clicked. }, "lastUpdated": "2024-04-25T21:28:10.877Z", "nextUpdate": "2024-04-25T21:39:10.877Z" }, "instagram": { "analytics": { /* * Demographics: The following audience demographic data is only available * for Intagram Users with at least 100 followers and engagements (last 30 days). * Additionally, audience demographic data is only available when * there is more than 100 people in each demographic category. */ "audienceCity": { "Sydney, New South Wales": 2, "London, England": 2, "Bridgewater, New Jersey": 1, "Puli, Nantou": 1 }, "audienceCityEngagedAudienceDemographics": { "KIRKLAND, WASHINGTON": 3, "LAKE BOSWORTH, WASHINGTON": 6, "WASHOUGAL, WASHINGTON": 1, "SEATTLE, WASHINGTON": 1, "BURLINGTON, WASHINGTON": 2 }, "audienceCountry": { "TW": 1, "HK": 5, "SG": 1, "AU": 2 }, "audienceCountryEngagedAudienceDemographics": { "DE": 0, "GB": 0, "US": 161 }, "audienceGenderAge": { "F.18-24": 1, "F.25-34": 6, "F.35-44": 13, "F.45-54": 16 }, "audienceGenderAgeEngagedAudienceDemographics": { "F.13-17": 2, "F.18-24": 11, "F.25-34": 15, "F.35-44": 24 }, "biography": "What to watch next? Get recommendations from your friends.", "commentsCount": 6, // Total comments of the past 500 posts "followersCount": 2, // Current total of followers - must have at least 100 followers to show "followsCount": 19, // Current total of users followed "id": "17941424040207809", "igId": 24372260340, // legacy Instagram user ID - only available when Instagram is linked via Facebook Page "igLoginId": "84739261504829371", // Instagram native user ID - only available when Instagram is linked via direct Instagram login "likeCount": 116, // Total like of the past 500 posts "mediaCount": 266, // Total media count on the account, always lifetime. Not filtered by `quarters` or `daily`. "name": "theGoodone", "profilePictureUrl": "https://scontent.fphl1-1.fna.fbcdn.net/v/t51.2885-15/74638883_536953567098493_7984001172816003072_n.jpg?_nc_cat=102&ccb=1-5&_nc_sid=86c713&_nc_ohc=krzDDPMjIZQAX-m1PsT&_nc_ht=scontent.fphl1-1.fna&edm=AL-3X8kEAAAA&oh=00_AT9uZhfkQyHWuThTgktvXxB8XEK_xV1_z72frRwtyGwHTA&oe=6246E46D", "reachCount": 1, // 180 day period - Total number of unique users who have viewed at least one media. Scalar value returned in non-daily mode; when daily=true a nested `reach` object is returned instead (see the Daily success example). "shareCount": 234, // Total number of shares on the Instagram account's content (posts, stories, reels, videos, live videos) aggregated per-day over the active date window. Follows the same 90-day rolling maximum as `viewsCount` and respects `quarters` / `daily`. Defaults to `0` for accounts where Meta does not return the `shares` metric. "username": "thegoodone", "viewsCount": 123212, // 90 day rolling window (maximum Meta allows on the User Metrics endpoint) - Total number of times the IG User's media have been viewed "website": "https://www.mywebsite.com/" }, "lastUpdated": "2022-05-09T00:52:30.530Z", "nextUpdate": "2022-05-09T02:07:30.530Z" }, /* LinkedIn Corporate: Lifetime analytics since start of account */ "linkedin": { // The likeCount and commentCount fields do not count likes or comments made by companies, including the company that authored the share and are not decremented when a like or comment is deleted. "analytics": { "clickCount": 10, "clicks": { // Only available for organizations that have career pages. This is a paid product. "mobileCareersPageClicks": { "careersPageJobsClicks": 2, "careersPagePromoLinksClicks": 0, "careersPageEmployeesClicks": 0 }, "careersPageClicks": { "careersPagePromoLinksClicks": 5, "careersPageBannerPromoClicks": 0, "careersPageJobsClicks": 0, "careersPageEmployeesClicks": 3 } }, "commentCount": 12, "engagement": 0.06060606060606061, // Number of organic clicks, likes, comments, and shares over impressions. "followers": { "organicFollowerCount": 7, "paidFollowerCount": 2, "totalFollowerCount": 12 // Total numuber might be higher than organic + paid. For total use this field. }, "impressionCount": 264, "likeCount": 6, // Number of likes. This field can become negative when members who liked a sponsored share later unlike it. "shareCount": 3, // Number of mentions of the company in a share across LinkedIn "uniqueImpressionsCount": 51, "views": { "aboutPageViews": 2, "allDesktopPageViews": 8, "allMobilePageViews": 8, "allPageViews": 16, "careersPageViews": 0, // Only available for organizations that have career pages "desktopAboutPageViews": 0, "desktopCareersPageViews": 0, "desktopInsightsPageViews": 0, "desktopJobsPageViews": 0, "desktopLifeAtPageViews": 0, "desktopOverviewPageViews": 8, "desktopPeoplePageViews": 0, "desktopProductsPageViews": 0, "insightsPageViews": 0, "jobsPageViews": 0, "lifeAtPageViews": 0, "mobileAboutPageViews": 2, "mobileCareersPageViews": 0, "mobileInsightsPageViews": 0, "mobileJobsPageViews": 0, "mobileLifeAtPageViews": 0, "mobileOverviewPageViews": 6, "mobilePeoplePageViews": 0, "mobileProductsPageViews": 0, "overviewPageViews": 14, "peoplePageViews": 0, "productsPageViews": 0 } }, "lastUpdated": "2022-05-09T00:52:30.530Z", "nextUpdate": "2022-05-09T02:07:30.530Z" }, /* LinkedIn Personal (member) */ "linkedin": { "description": "Founder", "id": "Z_yXaxh_Et", // LinkedIn Personal ID "name": "John Doe", "platform": "linkedin", "profileImageUrl": "https://media.licdn.com/dms/image/v2/C5103AQHORT70jVfKVA/profile", "url": "https://www.linkedin.com/in/johndoe", "userName": "johndoe", "analytics": { "commentCount": 410, // Aggregate comments "followersCount": 4210, // Lifetime follower count "followersDaily": [ // Daily follower growth (trailing 30 days) { "memberFollowersCount": 12, "dateRange": { "start": { "year": 2026, "month": 6, "day": 9 } } } ], "impressionCount": 98000, // Aggregate impressions across posts "likeCount": 2300, // Aggregate reactions "shareCount": 190, // Aggregate reshares "uniqueImpressionsCount": 41000 // Aggregate unique members reached } }, /* 90 days summary and daily details, board analytics full history */ "pinterest": { "analytics": { "board": { "boardPinsModifiedAt": "2023-03-14T22:32:06Z", "collaboratorCount": 0, "createdAt": "2019-03-07T22:02:22Z", "description": "", "followerCount": 24, "id": "4299544013183293", "media": { "pinThumbnailUrls": [ "https://i.pinimg.com/150x150/62/08/8b/62088b44a6152de7198efd3c2bc5c0.jpg", "https://i.pinimg.com/150x150/ac/b7/c9/acb7c9f8096317180c20022e9b9b8a.jpg", "https://i.pinimg.com/150x150/5a/d7/56/5ad756fb64a688934bd06853dff3a2.jpg", "https://i.pinimg.com/150x150/5a/d7/56/5ad755db64a688934bd06853dff3a2.jpg", "https://i.pinimg.com/150x150/a9/22/b6/a922ef5289639b617c24704d806998.jpg" ], "imageCoverUrl": "https://i.pinimg.com/400x300/f8/ec/14/f8ec14e7770a53b2e3595773564950.jpg" }, "name": "Places to Retire", "pinCount": 64, "privacy": "PUBLIC", "username": "glueUp" }, "clickthrough": 23, "clickthroughRate": 0.34, "closeup": 3, "closeupRate": 0.43, "engagement": 54, "engagementRate": 0.43, "fullScreenPlay": 23, "fullScreenPlaytime": 42, "impression": 12231, "outboundClick": 256, "outboundClickRate": 0.0.23, "pinClick": 32, "pinClickRate": 0.23, "quartile95PercentView": 42, "save": 45, "saveRate": 23, "video10sView": 902, "videoAvgWatchTime": 234, "videoMrcView": 23, "videoStart": 34, "videoV50WatchTime": 343, "daily": [ { "date": "2021-09-28", "metrics": { "SAVE_RATE": 0, "OUTBOUND_CLICK_RATE": 0, "IMPRESSION": 0, "CLOSEUP_RATE": 0, "ENGAGEMENT_RATE": 0, "VIDEO_10S_VIEW": 0, "VIDEO_AVG_WATCH_TIME": 0, "ENGAGEMENT": 0, "CLOSEUP": 0, "FULL_SCREEN_PLAY": 0, "SAVE": 0, "PIN_CLICK_RATE": 0, "OUTBOUND_CLICK": 0, "VIDEO_MRC_VIEW": 0, "VIDEO_V50_WATCH_TIME": 0, "QUARTILE_95_PERCENT_VIEW": 0, "VIDEO_START": 0, "CLICKTHROUGH": 0, "PIN_CLICK": 0, "CLICKTHROUGH_RATE": 0, "FULL_SCREEN_PLAYTIME": 0 } } ] }, "lastUpdated": "2022-05-09T00:52:30.530Z", "nextUpdate": "2022-05-09T02:07:30.530Z" }, /* Lifetime analytics since start of account */ "reddit": { "analytics": { "acceptFollowers": true, // Whether the user accepts new followers "awardeeKarma": 125, // Karma points received from Reddit awards given by others "awarderKarma": 45, // Karma points earned from giving awards to others "canCreateSubreddit": true, // Whether the user has permission to create new subreddits "coins": 250, // Number of Reddit coins the user currently has "commentKarma": 2847, // Karma points earned from comments "created": "2023-03-15T14:22:30.000Z", // Account creation date in ISO format "friends": 12, // Number of Reddit friends the user has added "hasSubscribed": true, // Whether the user has subscribed to any subreddits "hasVerifiedEmail": true, // Whether the user has verified their email address "hideFromRobots": false, // Whether the user has opted to hide their profile from search engines "iconImg": "https://www.redditstatic.com/avatars/default_5.png", // URL to the user's avatar image "id": "abc123def456", // Unique Reddit user ID "inboxCount": 3, // Number of unread messages in the user's inbox "isEmployee": false, // Whether the user is a Reddit employee "isGold": false, // Whether the user has Reddit Gold/Premium subscription "isMod": false, // Whether the user is a moderator of any subreddits "isSponsor": false, // Whether the user is a Reddit sponsor "isSuspended": false, // Whether the user's account is currently suspended "linkKarma": 1567, // Karma points earned from submitted links/posts "linkedIdentities": [ // Array of external identity providers linked to the account "https://accounts.google.com" ], "name": "RedditUser_Example123", // Reddit username "over18": true, // Whether the user is over 18 (affects NSFW content visibility) "profileImageSize": null, // Dimensions of the profile image (null if using default avatar) "profileImageUrl": "https://i.redd.it/1234567890.png", // URL to custom profile image (empty if using default avatar) "suspensionExpiration": null, // When account suspension expires (null if not suspended) "totalKarma": 4414, // Total karma points (sum of link karma and comment karma) "url": "https://www.reddit.com/user/RedditUser_Example123", // Direct URL to the user's Reddit profile "verified": true // Whether the user has a verified Reddit account }, "lastUpdated": "2025-07-29T00:31:47.080Z", "nextUpdate": "2025-07-29T00:42:47.080Z" }, "snapchat": { "analytics": [ { "adsSubscribes": 6, // Number of new subscribers acquired through ads "avgViewTime": 5430, // Average view time in milliseconds per viewer "favorites": 42, // Number of times content was favorited "interactions": 156, // Number of times users interacted with content (taps, swipes) "lensAvgViewTime": 8720, // Average view time in milliseconds for lens content "lensPlays": 387, // Number of times lenses were played "lensSubscribers": 18, // Total number of lens subscribers "lensSubscribes": 5, // New lens subscribers "lensUniques": 342, // Unique users who viewed lenses "lensViewTime": 2832800, // Total lens view time in milliseconds "lensViews": 432, // Total number of lens views "mediaId": "43548e97-edf1-44f9-984a-0a38470875bc", "playTime": 4268700, // Total play time in milliseconds "profilePaidViews": 176, // Profile views from paid promotions "profileViews": 834, // Total profile views "replies": 23, // Number of replies to snaps/stories "savedStoryAvgViewTime": 4950, // Average view time for saved stories in milliseconds "savedStoryFavorites": 19, // Number of favorites on saved stories "savedStorySnapCombinedUniques": 523, // Combined unique viewers of saved stories "savedStorySnapCombinedViews": 712, // Combined views of saved stories "savedStorySnapPaidUniques": 142, // Unique paid viewers of saved stories "savedStorySnapPaidViews": 178, // Number of paid views of saved stories "savedStorySubscribes": 8, // Number of subscribes from saved stories "savedStoryUniques": 381, // Unique viewers of saved stories "savedStoryViewTime": 1925850, // Total view time of saved stories in milliseconds "savedStoryViews": 534, // Total views of saved stories "scans": 98, // Number of Snapcode scans "screenshots": 12, // Total number of screenshots taken "shares": 36, // Number of times content was shared "snapCombinedUniques": 734, // Combined unique viewers across snaps "snapCombinedViews": 912, // Combined total views across snaps "snapPaidUniques": 203, // Unique users who viewed snaps through paid promotion "snapPaidViews": 267, // Number of paid snap views "snapViewTime": 3784200, // Total snap view time in milliseconds "socialUnlocks": 54, // Number of social unlocks "spotlightAvgViewTime": 7230, // Average view time for Spotlight content in milliseconds "spotlightCombinedUniques": 1247, // Combined unique viewers of Spotlight content "spotlightCombinedViews": 1672, // Combined views of Spotlight content "spotlightFavorites": 89, // Number of favorites on Spotlight content "spotlightPaidUniques": 324, // Unique paid viewers of Spotlight content "spotlightPaidViews": 412, // Number of paid views of Spotlight content "spotlightSubscribes": 17, // Number of subscribes from Spotlight content "spotlightUniques": 923, // Unique viewers of Spotlight content "spotlightViewTime": 6765290, // Total view time of Spotlight content in milliseconds "spotlightViews": 1260, // Total views of Spotlight content "storyAvgViewTime": 5180, // Average view time for stories in milliseconds "storyFavorites": 37, // Number of favorites on stories "storySubscribers": 63, // Total number of story subscribers "storySubscribes": 9, // New story subscribers "storyUniques": 532, // Unique viewers of stories "storyViews": 687, // Total views of stories "subscribers": 143, // Total number of subscribers "subscribes": 12, // New subscribers in the period "swipeDowns": 21, // Number of swipe downs "swipeUps": 43, // Number of swipe ups "uniqueScreenshots": 9, // Number of unique users who took screenshots "uniqueSessions": 672, // Number of unique sessions "unsubscribes": 3, // Number of unsubscribes in the period "viewTime": 4568300, // Total view time in milliseconds "viewers": 723, // Number of unique viewers "views": 879 // Total number of views } ], "lastUpdated": "2025-05-21T20:24:02.787Z", "nextUpdate": "2025-05-21T20:35:02.787Z" }, "threads": { "analytics": { "biography": "Post to social media with an API for yourself or your users: Instagram, Facebook, Twitter, TikTok, YouTube, and more", "followersCount": 123, "id": "92732926562", "isEligibleForGeoRestrictions": true, "isVerified": true, "likes": 1121, "name": "Ayrshare", "profileImageUrlHttps": "https://scontent.cdninstagram.com/v/t51.2885-15/357665262_1390047998231434_3968630539537390629_n.jpg?stp=dst-jpg_e35_tt6&_nc_cat=104&ccb=1-7&_nc_sid=18de74&_nc_ohc=CHlHbGcrCaAQ7kNvwFqZTDz&_nc_oc=Adkj3ocdD7bHn5qffGIB20t1C7icONIg8CS7t4Bcf6sssxxve-k94PkCEFDXGsY6ND0&_nc_zt=23&_nc_ht=scontent.cdninstagram.com&edm=AP4hL3IEAAAA&oh=00_AfTPLboqmm2iSOGMfvS29exnVLn5U8y9QPnL6cwLQIcZ9g&oe=68915ECD", "quotes": 2, "replies": 196, "reposts": 3, "username": "ayrshare", "views": 36721 }, "lastUpdated": "2025-04-27T15:40:28.885Z", "nextUpdate": "2025-04-27T15:51:28.885Z" }, "tiktok": { "analytics": { "addressClicks": 2, // Number of clicks collected on the Address button past 60 days "appDownloadClicks": 1, // Number of clicks collected on the App Download button past 60 days "audienceAges": [ // Updated by TikTok every 24-48 hours. Only available for TikTok accounts with at least 100 followers. { "percentage": 0.125, "age": "45-54" }, { "percentage": 0.053, "age": "55+" }, { "percentage": 0.138, "age": "18-24" }, { "percentage": 0.36, "age": "25-34" }, { "percentage": 0.324, "age": "35-44" } ], "audienceCountries": [ // Updated by TikTok every 24-48 hours. { "percentage": 0.25, "country": "NG" }, { "percentage": 0.75, "country": "US" } ], "audienceGenders": [ // Updated by TikTok every 24-48 hours. { "percentage": 0.25, "gender": "Female" }, { "percentage": 0.5, "gender": "Male" }, { "percentage": 0.25, "gender": "Other" } ], "bio": "My tiktok account", "bioLinkClicks": 2, // Number of clicks collected on the Bio Link button past 60 days "commentCountPeriod": "90 days",// quarters × 90 days when date range is active (e.g., "90 days", "180 days"); "60 days" when no date filter; "partial" when the post history could not be fully retrieved "commentCountTotal": 1807, // TikTok caps profile-level lookback at 60 days. When `quarters` is set, this is the sum of lifetime engagement on posts created within the window (not engagement that occurred within the window). "displayName": "Me and You", "durationAverage": "10.72", // Deprecated. Will be removed in the future. Updated every 12 hours "emailClicks": 0, // Number of clicks collected on the Email button past 60 days "followerCount": 34, // Current follower count "followingCount": 39, // Current following count "leadSubmissions": 0, // Number of leads submitted past 60 days "isBusinessAccount": true, // Whether the account is a business account "isVerified": false, // Whether TikTok has provided a verified badge to the account after confirming that it belongs to the user it represents "likeCountTotal": 2, // Genuine all-time total - the only TikTok profile metric that returns true lifetime data (sourced from TikTok's `total_likes` field). "phoneNumberClicks": 0, // Number of clicks collected on the Phone Number button past 60 days "profileViews": 79346, // Total profile views past 60 days "shareCountPeriod": "90 days", // quarters × 90 days when date range is active (e.g., "90 days", "180 days"); "60 days" when no date filter; "partial" when the post history could not be fully retrieved "shareCountTotal": 4, // TikTok caps profile-level lookback at 60 days. When `quarters` is set, this is the sum of lifetime engagement on posts created within the window (not engagement that occurred within the window). "url": "https://vm.tiktok.com/ZTRuw5kM6/", "userImage": "https://p16-sign-va.tiktokcdn.com/tos-maliva-avt-0068/f123c4b57351e266", "username": "@funnyone", "videoCountTotal": 18, // Video total video count "viewCountPeriod": "90 days", // quarters × 90 days when date range is active (e.g., "90 days", "180 days"); "60 days" when no date filter; "partial" when the post history could not be fully retrieved "viewCountTotal": 1493 // TikTok caps profile-level lookback at 60 days. When `quarters` is set, this is the sum of lifetime engagement on posts created within the window (not engagement that occurred within the window). }, "lastUpdated": "2022-05-09T00:52:30.530Z", "nextUpdate": "2022-05-09T02:07:30.530Z" }, /* Lifetime analytics since start of handle */ "twitter": { "analytics": { "created": "2019-11-12T15:22:57Z", "description": "The best X account", "displayName": "Ayrshare", "followersCount": 11253, "followingCount": 4242, "id": 11842743387998232, "isIdentityVerified": false, // Whether the account is a verified account "likeCount": 561, // The number of likes created by this user "listedCount": 17, // The number of lists that include this user "location": "New York, NY", "mostRecentTweetId": "1848848832072667321", "name": "ayrshare", "parody": false, // Whether the account is a parody account "pinnedTweet": { "text": "Happy Days!", "id": "184884883207266733342", "editHistoryTweetIds": [ "18488488320726673223" ] }, "profileBannerUrl": "https://pbs.twimg.com/profile_banners/87253/15041912", "profileImageUrl": "http://pbs.twimg.com/profile_images/1184/lvQZPpt_normal.png", "protected": false, // User has chosen to protect their posts (private posts) "receivesYourDM": true, // User has chosen to receive direct messages from others "subscription": { "subscribesToYou": false }, "subscriptionType": "Premium", // Premium, PremiumPlus, None "tweetCount": 2930, // The number of Tweets (including retweets) issued by the user "url": "https://t.co/2XqeJiBy", "username": "ayrshare", "verified": true, // When true, indicates that the user has a verified account "verifiedType": "blue", // "blue", "business", "none" "website": "https://www.wondrouswaffles.com/" }, "lastUpdated": "2022-05-09T00:52:30.530Z", "nextUpdate": "2022-05-09T02:07:30.530Z" }, "youtube": { /* Lifetime analytics since start of channel */ "analytics": { "averageViewDuration": 99, // The average length, in seconds, of video playbacks. "averageViewPercentage": 127.06, // The average percentage of a video watched during a video playback. "comments": 8, "created": "2010-01-10T15:30:57Z", "description": "This is the channel you want", "dislikes": 1, // The number of times that users indicated that they disliked a video by giving it a negative rating. "engagedViews": 41, // The number of views counted past the initial seconds of playback — YouTube's view-counting methodology prior to August 24, 2026. "estimatedMinutesWatched": 72, // The number of minutes that users watched videos for the specified channel, content owner, video, or playlist. "hiddenSubscriberCount": false, // Indicates whether the channel's subscriber count is publicly visible. "isLinked": true, "likes": 3, // Sum of likes across all videos on the channel within the date window determined by `quarters` (default: last 360 days). YouTube does not expose a lifetime channel-wide like counter. For a lifetime total, pass `youtube: { lifetime: true }` to get the `lifetimeLikes` field. "lifetimeLikes": 8750, // Opt-in lifetime sum of likes across every public video on the channel. Shown here because this sample assumes the request included `youtube: { lifetime: true }` — the broad request example at the top of this page does NOT include that flag, so `lifetimeLikes` will be absent from responses to that example request. May be `null` if channel exceeds 1,000-video threshold. Cached for 24 hours per channel. Deleted and private videos are excluded. "longUploadsStatus": "allowed", "playlistId": "UUsp6CnxiNbUUYKZwXQ", // Primary playlist "playlists": [ // All playlists associated with the connected channel { "description": "Playlist 1", "id": "PLcLpmVOb3fDaf7", "publishedAt": "2022-10-21T21:05:55Z", "title": "Super Playlist", "url": "https://www.youtube.com/playlist?list=PLcLpmVOb3fDaf7" }, { "description": "Playlist 2", "id": "FLsp6CnxiNbU", "publishedAt": "2012-02-26T19:16:55Z", "title": "Favorites", "url": "https://www.youtube.com/playlist?list=FLsp6CnxiNbU" } ], "privacyStatus": "public", // "public", "private", or "unlisted" "shares": 34, // The number of times that users shared a video through the Share button "subscriberCount": "67", // The number of subscribers that the channel has. "subscribersGained": 34, // The number of times that users subscribed to a channel. "subscribersLost": 2, // The number of times that users unsubscribed from a channel. "thumbnailUrl": "https://yt3.ggpht.com/ytc/AMLnZu_lgyzY2EFDAP-no-rj", "title": "The Best Channel", "url": "https://www.youtube.com/@youtube53", // The Channel URL using the custom handle "videoCount": "202", "videosAddedToPlaylists": 23, "videosRemovedFromPlaylists": 2, "viewCount": "5", // The sum of the number of times all the videos in all formats have been viewed for a channel for all time. "views": 44 // The number of video views that occurred in the last time period (quarters) }, "quarters": 4, "lastUpdated": "2022-05-09T00:52:30.530Z", "nextUpdate": "2022-05-09T02:07:30.530Z" } // The top-level `warnings` array (see the "warnings" Info block above) // would appear here as a sibling of `youtube`/`instagram`/etc. only when // a non-fatal condition applies — e.g. `lifetimeLikes` being skipped // because a channel exceeds the 1,000-video threshold. It is omitted in // this success example because `lifetimeLikes: 8750` was computed // successfully above. } ``` ```json 200: Success Daily {4, 459, 497, 555, 1474, 1716} theme={"system"} // Available for Facebook, Google Business Profile, Instagram, Snapchat, TikTok, and YouTube // Recommended: https://www.ayrshare.com/docs/apis/overview#compression { "facebook": { "analytics": { "about": "One of the best stie ever", "category": "Local Business", "emails": [ "hello@ayrshare.com" ], "fanCount": 48519, "followersCount": 48681, "id": "885526541490222", "instagramBusinessAccount": { "id": "1784145407231999" }, "isPublished": true, "link": "https://www.facebook.com/885526541490222", "location": { // Physical location details for the Page "city": "Rome", "country": "Italy", "latitude": 41.853696407098, "longitude": 12.573806071995, "street": "Via Vincenzo", "zip": "00174" }, "name": "Super Site", "overallStarRating": 0, // The average star rating of the page. "pageFollows": { "period": "day", "values": [ { "value": 3, "endTime": "2025-08-12T07:00:00.000Z" }, { "value": 3, "endTime": "2025-08-13T07:00:00.000Z" }, ], "total": 3 }, "pageMediaView": { "period": "day", "values": [ { "value": 100, "endTime": "2025-08-12T07:00:00.000Z" }, { "value": 200, "endTime": "2025-08-13T07:00:00.000Z" } ], "total": 300 }, "pageMediaViewIsFromAds": { "period": "day", "values": [ { "value": 22, "endTime": "2025-08-12T07:00:00.000Z" }, { "value": 41, "endTime": "2025-08-13T07:00:00.000Z" } ], "total": 63 }, "pageMediaViewIsFromFollowers": { "period": "day", "values": [ { "value": 67, "endTime": "2025-08-12T07:00:00.000Z" }, { "value": 29, "endTime": "2025-08-13T07:00:00.000Z" } ], "total": 96 }, "pagePostEngagements": { // The number of interactions with your posts such as likes, comments, shares and more "period": "day", "total": 0, "values": [ { "value": 0, "endTime": "2024-11-03T07:00:00.000Z" }, { "value": 0, "endTime": "2024-11-04T08:00:00.000Z" } ] }, "pageVideoCompleteViews30S": { // The number of times videos played for at least 30 seconds "period": "day", "total": 0, "values": [ { "value": 0, "endTime": "2024-11-03T07:00:00.000Z" }, { "value": 0, "endTime": "2024-11-04T08:00:00.000Z" } ] }, "pageVideoCompleteViews30SAutoplayed": { // The number of times videos played for at least 30 seconds when autoplayed "period": "day", "total": 0, "values": [ { "value": 0, "endTime": "2024-11-03T07:00:00.000Z" }, { "value": 0, "endTime": "2024-11-04T08:00:00.000Z" } ] }, "pageVideoCompleteViews30SClickToPlay": { // The number of times videos played for at least 30 seconds after clicking play "period": "day", "total": 0, "values": [ { "value": 0, "endTime": "2024-11-03T07:00:00.000Z" }, { "value": 0, "endTime": "2024-11-04T08:00:00.000Z" } ] }, "pageVideoCompleteViews30SOrganic": { // The number of times videos played for at least 30 seconds through organic reach "period": "day", "total": 0, "values": [ { "value": 0, "endTime": "2024-11-03T07:00:00.000Z" }, { "value": 0, "endTime": "2024-11-04T08:00:00.000Z" } ] }, "pageVideoCompleteViews30SPaid": { // The number of times videos played for at least 30 seconds through paid reach "period": "day", "total": 0, "values": [ { "value": 0, "endTime": "2024-11-03T07:00:00.000Z" }, { "value": 0, "endTime": "2024-11-04T08:00:00.000Z" } ] }, "pageVideoCompleteViews30SRepeatViews": { // The number of times videos were replayed after playing for at least 30 seconds "period": "day", "total": 0, "values": [ { "value": 0, "endTime": "2024-11-03T07:00:00.000Z" }, { "value": 0, "endTime": "2024-11-04T08:00:00.000Z" } ] }, "pageVideoCompleteViews30SUnique": { // The number of unique people who watched your videos for at least 30 seconds "period": "day", "total": 0, "values": [ { "value": 0, "endTime": "2024-11-03T07:00:00.000Z" }, { "value": 0, "endTime": "2024-11-04T08:00:00.000Z" } ] }, "pageVideoRepeatViews": { // The number of times videos were replayed "period": "day", "total": 0, "values": [ { "value": 0, "endTime": "2024-11-03T07:00:00.000Z" }, { "value": 0, "endTime": "2024-11-04T08:00:00.000Z" } ] }, "pageVideoViewTime": { // Total time in seconds that videos were viewed "period": "day", "total": 2.33, "values": [ { "value": 2.33, "endTime": "2024-11-03T07:00:00.000Z" }, { "value": 0, "endTime": "2024-11-04T08:00:00.000Z" } ] }, "pageVideoViews": { // The number of times your videos were viewed "period": "day", "total": 0, "values": [ { "value": 0, "endTime": "2024-11-03T07:00:00.000Z" }, { "value": 0, "endTime": "2024-11-04T08:00:00.000Z" } ] }, "pageVideoViews10S": { // The number of times videos played for at least 10 seconds "period": "day", "total": 0, "values": [ { "value": 0, "endTime": "2024-11-03T07:00:00.000Z" }, { "value": 0, "endTime": "2024-11-04T08:00:00.000Z" } ] }, "pageVideoViews10SAutoplayed": { // The number of times videos played for at least 10 seconds when autoplayed "period": "day", "total": 0, "values": [ { "value": 0, "endTime": "2024-11-03T07:00:00.000Z" }, { "value": 0, "endTime": "2024-11-04T08:00:00.000Z" } ] }, "pageVideoViews10SClickToPlay": { // The number of times videos played for at least 10 seconds after clicking play "period": "day", "total": 0, "values": [ { "value": 0, "endTime": "2024-11-03T07:00:00.000Z" }, { "value": 0, "endTime": "2024-11-04T08:00:00.000Z" } ] }, "pageVideoViews10SOrganic": { // The number of times videos played for at least 10 seconds through organic reach "period": "day", "total": 0, "values": [ { "value": 0, "endTime": "2024-11-03T07:00:00.000Z" }, { "value": 0, "endTime": "2024-11-04T08:00:00.000Z" } ] }, "pageVideoViews10SPaid": { // The number of times videos played for at least 10 seconds through paid reach "period": "day", "total": 0, "values": [ { "value": 0, "endTime": "2024-11-03T07:00:00.000Z" }, { "value": 0, "endTime": "2024-11-04T08:00:00.000Z" } ] }, "pageVideoViews10SRepeatViews": { // The number of times videos were replayed after playing for at least 10 seconds "period": "day", "total": 0, "values": [ { "value": 0, "endTime": "2024-11-03T07:00:00.000Z" }, { "value": 0, "endTime": "2024-11-04T08:00:00.000Z" } ] }, "pageVideoViews10SUnique": { // The number of unique people who watched your videos for at least 10 seconds "period": "day", "total": 0, "values": [ { "value": 0, "endTime": "2024-11-03T07:00:00.000Z" }, { "value": 0, "endTime": "2024-11-04T08:00:00.000Z" } ] }, "pageVideoViewsAutoplayed": { // The number of times your videos autoplayed "period": "day", "total": 0, "values": [ { "value": 0, "endTime": "2024-11-03T07:00:00.000Z" }, { "value": 0, "endTime": "2024-11-04T08:00:00.000Z" } ] }, "pageVideoViewsByPaidNonPaid": { // Breakdown of video views by paid vs non-paid distribution "period": "day", "values": [ { "value": { "total": 0, "unpaid": 0, "paid": 0 }, "endTime": "2024-11-03T07:00:00.000Z" }, { "value": { "total": 0, "unpaid": 0, "paid": 0 }, "endTime": "2024-11-04T08:00:00.000Z" } ] }, "pageVideoViewsByUploadedHosted": { // Breakdown of video views by uploaded vs hosted content "period": "day", "values": [ { "value": { "pageUploaded": 0, "pageUploadedFromCrossposts": 0, "pageUploadedFromShares": 0, "pageHostedCrosspost": 0, "pageHostedShare": 0, "pageOwned": 0 }, "endTime": "2024-11-03T07:00:00.000Z" }, { "value": { "pageUploaded": 0, "pageUploadedFromCrossposts": 0, "pageUploadedFromShares": 0, "pageHostedCrosspost": 0, "pageHostedShare": 0, "pageOwned": 0 }, "endTime": "2024-11-04T08:00:00.000Z" } ] }, "pageVideoViewsClickToPlay": { // The number of times people clicked play to view your videos "period": "day", "total": 0, "values": [ { "value": 0, "endTime": "2024-11-03T07:00:00.000Z" }, { "value": 0, "endTime": "2024-11-04T08:00:00.000Z" } ] }, "pageVideoViewsOrganic": { // The number of times your videos were viewed through organic reach "period": "day", "total": 0, "values": [ { "value": 0, "endTime": "2024-11-03T07:00:00.000Z" }, { "value": 0, "endTime": "2024-11-04T08:00:00.000Z" } ] }, "pageVideoViewsPaid": { // The number of times your videos were viewed through paid reach "period": "day", "total": 0, "values": [ { "value": 0, "endTime": "2024-11-03T07:00:00.000Z" }, { "value": 0, "endTime": "2024-11-04T08:00:00.000Z" } ] }, // pageVideoViewsUnique was retired by Meta (June 15, 2026); see the note at the top of this page. "phone": "+39067265341", "reactions": { // Breakdown of reactions on your Page's content "like": 142, "love": 9, "wow": 1, "haha": 0, "sorry": 0, "anger": 0, "total": 152 }, "unreadMessageCount": 0, "username": "ayrshare", "verified": false, "website": "http://www.ayrshare.com" }, "quarters": 1, "lastUpdated": "2024-03-19T17:48:21.329Z", "nextUpdate": "2024-03-19T17:59:21.329Z" }, "gmb": { // Google Business Profile daily values - date range window determined by quarters parameter (90 days per quarter) "analytics": { "businessBookings": { "period": "day", "values": [ { "value": 0, "endTime": "2026-09-05T00:00:00.000Z" }, { "value": 0, "endTime": "2026-09-06T00:00:00.000Z" } ], "total": 0 }, "businessConversations": { "period": "day", "values": [ ... ], "total": 0 }, "businessDirectionRequests": { "period": "day", "values": [ { "value": 4, "endTime": "2026-09-05T00:00:00.000Z" }, { "value": 7, "endTime": "2026-09-06T00:00:00.000Z" } ], "total": 11 }, "businessFoodOrders": { "period": "day", "values": [ ... ], "total": 0 }, "businessImpressionsDesktopMaps": { "period": "day", "values": [ ... ], "total": 12 }, "businessImpressionsDesktopSearch": { "period": "day", "values": [ ... ], "total": 803 }, "businessImpressionsMobileMaps": { "period": "day", "values": [ ... ], "total": 9 }, "businessImpressionsMobileSearch": { "period": "day", "values": [ ... ], "total": 154 }, "callClicks": { "period": "day", "values": [ ... ], "total": 1 }, "websiteClicks": { "period": "day", "values": [ { "value": 2, "endTime": "2026-09-05T00:00:00.000Z" }, // Days Google reports no data for are returned with "value": 0 { "value": 0, "endTime": "2026-09-06T00:00:00.000Z" } ], "total": 2 } }, "quarters": 1, "lastUpdated": "2026-09-07T10:12:04.101Z", "nextUpdate": "2026-09-07T10:23:04.101Z" }, "instagram": { "analytics": { "audienceCity": { "KIRKLAND, WASHINGTON": 7, "LAKE BOSWORTH, WASHINGTON": 20, "BURLINGTON, WASHINGTON": 5, "SEATTLE, WASHINGTON": 22, "ARLINGTON HEIGHTS, WASHINGTON": 7, }, "audienceCountry": { "MM": 1, "CA": 2, "US": 2201, "LK": 1 }, "audienceGenderAge": { "F.13-17": 11, "F.18-24": 83, "M.13-17": 9, "M.18-24": 85, "M.65+": 10, "U.55-64": 25, "U.65+": 12 }, "biography": "Official Instagram feed for the best IG account", "commentsCount": 552, "followersCount": 2299, "followsCount": 26, "id": "17841407938064444", "igId": 79483284444, // legacy Instagram user ID - only available when Instagram is linked via Facebook Page "igLoginId": "84739261504829371", // Instagram native user ID - only available when Instagram is linked via direct Instagram login "likeCount": 30808, "mediaCount": 537, "name": "Best IG Account", "profilePictureUrl": "https://scontent-lga3-1.xx.fbcdn.net/v/t51.2885-15", "reach": { // Returned only when daily=true (replaces the scalar `reachCount` from non-daily mode) "period": "day", // Granularity of the time-series buckets "values": [ // Per-day reach time-series { "value": 387, "endTime": "2024-07-23T07:00:00.000Z" }, { "value": 46, "endTime": "2024-07-24T07:00:00.000Z" }, { "value": 4, "endTime": "2024-07-25T07:00:00.000Z" } ] }, "username": "instagram", "website": "https://www.instagram.com" }, "lastUpdated": "2024-07-25T16:09:33.608Z", "nextUpdate": "2024-07-25T16:20:33.608Z" }, "snapchat": { "analytics": [ { "adsSubscribes": { "period": "day", "values": [ { "endTime": "2026-01-21T00:00:00Z", "value": 0 }, { "endTime": "2026-01-22T00:00:00Z", "value": 0 } ], "total": 0 }, "avgViewTime": { "period": "day", "values": [ { "endTime": "2026-01-21T00:00:00Z", "value": 7126 }, { "endTime": "2026-01-22T00:00:00Z", "value": 7003 } ], "total": 14129 }, "favorites": { "period": "day", "values": [ { "endTime": "2026-01-21T00:00:00Z", "value": 4 }, { "endTime": "2026-01-22T00:00:00Z", "value": 3 } ], "total": 7 }, "interactions": { "period": "day", "values": [ { "endTime": "2026-01-21T00:00:00Z", "value": 8 }, { "endTime": "2026-01-22T00:00:00Z", "value": 22 } ], "total": 30 }, "lensAvgViewTime": { "period": "day", "values": [ { "endTime": "2026-01-21T00:00:00Z", "value": 3712 }, { "endTime": "2026-01-22T00:00:00Z", "value": 7837 } ], "total": 11549 }, "lensDauOverMau": { "period": "day", "values": [ { "endTime": "2026-01-21T00:00:00Z", "value": 0.074 }, { "endTime": "2026-01-22T00:00:00Z", "value": 0.042 } ], "total": 0.116 }, "lensDauOverWau": { "period": "day", "values": [ { "endTime": "2026-01-21T00:00:00Z", "value": 0.194 }, { "endTime": "2026-01-22T00:00:00Z", "value": 0.251 } ], "total": 0.445 }, "lensPlays": { "period": "day", "values": [ { "endTime": "2026-01-21T00:00:00Z", "value": 11 }, { "endTime": "2026-01-22T00:00:00Z", "value": 45 } ], "total": 56 }, "lensSubscribers": { "period": "day", "values": [ { "endTime": "2026-01-21T00:00:00Z", "value": 206 }, { "endTime": "2026-01-22T00:00:00Z", "value": 207 } ], "total": 413 }, "lensSubscribersGained": { "period": "day", "values": [ { "endTime": "2026-01-21T00:00:00Z", "value": 3 }, { "endTime": "2026-01-22T00:00:00Z", "value": 1 } ], "total": 4 }, "lensSubscribes": { "period": "day", "values": [ { "endTime": "2026-01-21T00:00:00Z", "value": 3 }, { "endTime": "2026-01-22T00:00:00Z", "value": 2 } ], "total": 5 }, "lensUniques": { "period": "day", "values": [ { "endTime": "2026-01-21T00:00:00Z", "value": 30 }, { "endTime": "2026-01-22T00:00:00Z", "value": 5 } ], "total": 35 }, "lensViewTime": { "period": "day", "values": [ { "endTime": "2026-01-21T00:00:00Z", "value": 1153 }, { "endTime": "2026-01-22T00:00:00Z", "value": 2231 } ], "total": 3384 }, "lensViews": { "period": "day", "values": [ { "endTime": "2026-01-21T00:00:00Z", "value": 29 }, { "endTime": "2026-01-22T00:00:00Z", "value": 25 } ], "total": 54 }, "mediaId": "6e9193cd-5013-478e-a272-d3d0e881f53f", "playTime": { "period": "day", "values": [ { "endTime": "2026-01-21T00:00:00Z", "value": 5273 }, { "endTime": "2026-01-22T00:00:00Z", "value": 5763 } ], "total": 11036 }, "profilePaidViews": { "period": "day", "values": [ { "endTime": "2026-01-21T00:00:00Z", "value": 2 }, { "endTime": "2026-01-22T00:00:00Z", "value": 0 } ], "total": 2 }, "profileViews": { "period": "day", "values": [ { "endTime": "2026-01-21T00:00:00Z", "value": 25 }, { "endTime": "2026-01-22T00:00:00Z", "value": 44 } ], "total": 69 }, "replies": { "period": "day", "values": [ { "endTime": "2026-01-21T00:00:00Z", "value": 0 }, { "endTime": "2026-01-22T00:00:00Z", "value": 2 } ], "total": 2 }, "savedStoryAvgViewTime": { "period": "day", "values": [ { "endTime": "2026-01-21T00:00:00Z", "value": 5471 }, { "endTime": "2026-01-22T00:00:00Z", "value": 3408 } ], "total": 8879 }, "savedStoryFavorites": { "period": "day", "values": [ { "endTime": "2026-01-21T00:00:00Z", "value": 2 }, { "endTime": "2026-01-22T00:00:00Z", "value": 0 } ], "total": 2 }, "savedStorySnapCombinedUniques": { "period": "day", "values": [ { "endTime": "2026-01-21T00:00:00Z", "value": 19 }, { "endTime": "2026-01-22T00:00:00Z", "value": 22 } ], "total": 41 }, "savedStorySnapCombinedViews": { "period": "day", "values": [ { "endTime": "2026-01-21T00:00:00Z", "value": 13 }, { "endTime": "2026-01-22T00:00:00Z", "value": 39 } ], "total": 52 }, "savedStorySnapPaidUniques": { "period": "day", "values": [ { "endTime": "2026-01-21T00:00:00Z", "value": 3 }, { "endTime": "2026-01-22T00:00:00Z", "value": 0 } ], "total": 3 }, "savedStorySnapPaidViews": { "period": "day", "values": [ { "endTime": "2026-01-21T00:00:00Z", "value": 8 }, { "endTime": "2026-01-22T00:00:00Z", "value": 4 } ], "total": 12 }, "savedStorySubscribes": { "period": "day", "values": [ { "endTime": "2026-01-21T00:00:00Z", "value": 2 }, { "endTime": "2026-01-22T00:00:00Z", "value": 2 } ], "total": 4 }, "savedStoryUniques": { "period": "day", "values": [ { "endTime": "2026-01-21T00:00:00Z", "value": 16 }, { "endTime": "2026-01-22T00:00:00Z", "value": 23 } ], "total": 39 }, "savedStoryViewTime": { "period": "day", "values": [ { "endTime": "2026-01-21T00:00:00Z", "value": 2287 }, { "endTime": "2026-01-22T00:00:00Z", "value": 4385 } ], "total": 6672 }, "savedStoryViews": { "period": "day", "values": [ { "endTime": "2026-01-21T00:00:00Z", "value": 14 }, { "endTime": "2026-01-22T00:00:00Z", "value": 12 } ], "total": 26 }, "scans": { "period": "day", "values": [ { "endTime": "2026-01-21T00:00:00Z", "value": 4 }, { "endTime": "2026-01-22T00:00:00Z", "value": 5 } ], "total": 9 }, "screenshots": { "period": "day", "values": [ { "endTime": "2026-01-21T00:00:00Z", "value": 0 }, { "endTime": "2026-01-22T00:00:00Z", "value": 6 } ], "total": 6 }, "shares": { "period": "day", "values": [ { "endTime": "2026-01-21T00:00:00Z", "value": 4 }, { "endTime": "2026-01-22T00:00:00Z", "value": 2 } ], "total": 6 }, "snapCombinedUniques": { "period": "day", "values": [ { "endTime": "2026-01-21T00:00:00Z", "value": 39 }, { "endTime": "2026-01-22T00:00:00Z", "value": 32 } ], "total": 71 }, "snapCombinedViews": { "period": "day", "values": [ { "endTime": "2026-01-21T00:00:00Z", "value": 88 }, { "endTime": "2026-01-22T00:00:00Z", "value": 111 } ], "total": 199 }, "snapPaidUniques": { "period": "day", "values": [ { "endTime": "2026-01-21T00:00:00Z", "value": 15 }, { "endTime": "2026-01-22T00:00:00Z", "value": 7 } ], "total": 22 }, "snapPaidViews": { "period": "day", "values": [ { "endTime": "2026-01-21T00:00:00Z", "value": 10 }, { "endTime": "2026-01-22T00:00:00Z", "value": 16 } ], "total": 26 }, "snapViewTime": { "period": "day", "values": [ { "endTime": "2026-01-21T00:00:00Z", "value": 3455 }, { "endTime": "2026-01-22T00:00:00Z", "value": 2858 } ], "total": 6313 }, "socialUnlocks": { "period": "day", "values": [ { "endTime": "2026-01-21T00:00:00Z", "value": 2 }, { "endTime": "2026-01-22T00:00:00Z", "value": 0 } ], "total": 2 }, "spotlightAvgViewTime": { "period": "day", "values": [ { "endTime": "2026-01-21T00:00:00Z", "value": 8990 }, { "endTime": "2026-01-22T00:00:00Z", "value": 5401 } ], "total": 14391 }, "spotlightCombinedUniques": { "period": "day", "values": [ { "endTime": "2026-01-21T00:00:00Z", "value": 44 }, { "endTime": "2026-01-22T00:00:00Z", "value": 25 } ], "total": 69 }, "spotlightCombinedViews": { "period": "day", "values": [ { "endTime": "2026-01-21T00:00:00Z", "value": 45 }, { "endTime": "2026-01-22T00:00:00Z", "value": 84 } ], "total": 129 }, "spotlightFavorites": { "period": "day", "values": [ { "endTime": "2026-01-21T00:00:00Z", "value": 3 }, { "endTime": "2026-01-22T00:00:00Z", "value": 2 } ], "total": 5 }, "spotlightPaidUniques": { "period": "day", "values": [ { "endTime": "2026-01-21T00:00:00Z", "value": 9 }, { "endTime": "2026-01-22T00:00:00Z", "value": 4 } ], "total": 13 }, "spotlightPaidViews": { "period": "day", "values": [ { "endTime": "2026-01-21T00:00:00Z", "value": 13 }, { "endTime": "2026-01-22T00:00:00Z", "value": 4 } ], "total": 17 }, "spotlightSubscribes": { "period": "day", "values": [ { "endTime": "2026-01-21T00:00:00Z", "value": 0 }, { "endTime": "2026-01-22T00:00:00Z", "value": 0 } ], "total": 0 }, "spotlightUniques": { "period": "day", "values": [ { "endTime": "2026-01-21T00:00:00Z", "value": 33 }, { "endTime": "2026-01-22T00:00:00Z", "value": 18 } ], "total": 51 }, "spotlightViewTime": { "period": "day", "values": [ { "endTime": "2026-01-21T00:00:00Z", "value": 6286 }, { "endTime": "2026-01-22T00:00:00Z", "value": 5193 } ], "total": 11479 }, "spotlightViews": { "period": "day", "values": [ { "endTime": "2026-01-21T00:00:00Z", "value": 24 }, { "endTime": "2026-01-22T00:00:00Z", "value": 33 } ], "total": 57 }, "storyAvgViewTime": { "period": "day", "values": [ { "endTime": "2026-01-21T00:00:00Z", "value": 6740 }, { "endTime": "2026-01-22T00:00:00Z", "value": 6862 } ], "total": 13602 }, "storyFavorites": { "period": "day", "values": [ { "endTime": "2026-01-21T00:00:00Z", "value": 4 }, { "endTime": "2026-01-22T00:00:00Z", "value": 2 } ], "total": 6 }, "storySubscribers": { "period": "day", "values": [ { "endTime": "2026-01-21T00:00:00Z", "value": 546 }, { "endTime": "2026-01-22T00:00:00Z", "value": 547 } ], "total": 1093 }, "storySubscribersGained": { "period": "day", "values": [ { "endTime": "2026-01-21T00:00:00Z", "value": 3 }, { "endTime": "2026-01-22T00:00:00Z", "value": 3 } ], "total": 6 }, "storySubscribes": { "period": "day", "values": [ { "endTime": "2026-01-21T00:00:00Z", "value": 2 }, { "endTime": "2026-01-22T00:00:00Z", "value": 1 } ], "total": 3 }, "storyUniques": { "period": "day", "values": [ { "endTime": "2026-01-21T00:00:00Z", "value": 9 }, { "endTime": "2026-01-22T00:00:00Z", "value": 13 } ], "total": 22 }, "storyViews": { "period": "day", "values": [ { "endTime": "2026-01-21T00:00:00Z", "value": 18 }, { "endTime": "2026-01-22T00:00:00Z", "value": 25 } ], "total": 43 }, "subscribers": { "period": "day", "values": [ { "endTime": "2026-01-21T00:00:00Z", "value": 273 }, { "endTime": "2026-01-22T00:00:00Z", "value": 274 } ], "total": 547 }, "subscribersGained": { "period": "day", "values": [ { "endTime": "2026-01-21T00:00:00Z", "value": 4 }, { "endTime": "2026-01-22T00:00:00Z", "value": 2 } ], "total": 6 }, "subscribes": { "period": "day", "values": [ { "endTime": "2026-01-21T00:00:00Z", "value": 3 }, { "endTime": "2026-01-22T00:00:00Z", "value": 3 } ], "total": 6 }, "swipeDowns": { "period": "day", "values": [ { "endTime": "2026-01-21T00:00:00Z", "value": 6 }, { "endTime": "2026-01-22T00:00:00Z", "value": 4 } ], "total": 10 }, "swipeUps": { "period": "day", "values": [ { "endTime": "2026-01-21T00:00:00Z", "value": 5 }, { "endTime": "2026-01-22T00:00:00Z", "value": 11 } ], "total": 16 }, "uniqueScreenshots": { "period": "day", "values": [ { "endTime": "2026-01-21T00:00:00Z", "value": 3 }, { "endTime": "2026-01-22T00:00:00Z", "value": 0 } ], "total": 3 }, "uniqueSessions": { "period": "day", "values": [ { "endTime": "2026-01-21T00:00:00Z", "value": 34 }, { "endTime": "2026-01-22T00:00:00Z", "value": 11 } ], "total": 45 }, "unsubscribes": { "period": "day", "values": [ { "endTime": "2026-01-21T00:00:00Z", "value": 0 }, { "endTime": "2026-01-22T00:00:00Z", "value": 0 } ], "total": 0 }, "viewTime": { "period": "day", "values": [ { "endTime": "2026-01-21T00:00:00Z", "value": 11140 }, { "endTime": "2026-01-22T00:00:00Z", "value": 7310 } ], "total": 18450 }, "viewers": { "period": "day", "values": [ { "endTime": "2026-01-21T00:00:00Z", "value": 42 }, { "endTime": "2026-01-22T00:00:00Z", "value": 53 } ], "total": 95 }, "views": { "period": "day", "values": [ { "endTime": "2026-01-21T00:00:00Z", "value": 33 }, { "endTime": "2026-01-22T00:00:00Z", "value": 74 } ], "total": 107 } } ], "lastUpdated": "2026-04-20T13:13:47.646Z", "nextUpdate": "2026-04-20T13:24:47.646Z" }, "tiktok": { // TikTok Analytics with "day" period - date range window determined by quarters parameter "analytics": { "addressClicks": { // Number of clicks collected on the Address button past 60 days "period": "day", "total": 0, "values": [ { "value": 0, "endTime": "2025-01-21T00:00:00.000Z" }, { "value": 0, "endTime": "2025-01-20T00:00:00.000Z" } ] }, "appDownloadClicks": { // Number of clicks collected on the App Download button past 60 days "period": "day", "total": 0, "values": [ { "value": 0, "endTime": "2025-01-21T00:00:00.000Z" }, { "value": 0, "endTime": "2025-01-20T00:00:00.000Z" } ] }, "audienceActivity": { // Hourly follower activity. Updated by TikTok every 24-48 hours after posting. Only available for TikTok accounts with at least 100 followers. "period": "day", "total": 14824, "values": [ { "value": [ { "count": 12386, "hour": "5" }, { "count": 2438, "hour": "9" } ], "endTime": "2025-01-21T00:00:00.000Z" } ] }, "audienceAges": [ // Updated by TikTok every 24-48 hours after posting. Only available for TikTok accounts with at least 100 followers. { "percentage": 0.125, "age": "45-54" }, { "percentage": 0.053, "age": "55+" }, { "percentage": 0.138, "age": "18-24" }, { "percentage": 0.36, "age": "25-34" }, { "percentage": 0.324, "age": "35-44" } ], "audienceCountries": [ { "country": "SV", "percentage": 0.003 }, { "country": "US", "percentage": 0.808 } ], "audienceGenders": [ { "percentage": 0.76, "gender": "Female" }, { "percentage": 0.24, "gender": "Male" }, { "percentage": 0, "gender": "Other" } ], "bio": "This is the bio of the TikTok account", "bioLinkClicks": { // Number of clicks collected on the Bio Link button past 60 days "period": "day", "total": 0, "values": [ { "value": 0, "endTime": "2025-01-21T00:00:00.000Z" }, { "value": 0, "endTime": "2025-01-20T00:00:00.000Z" } ] }, "commentCountTotal": { // Total comment count past 60 days "period": "day", "values": [ { "value": 6, "endTime": "2025-01-21T00:00:00.000Z" }, { "value": 6, "endTime": "2025-01-20T00:00:00.000Z" } ] }, "displayName": "ayrshare", "emailClicks": { // Number of clicks collected on the Email button past 60 days "period": "day", "values": [ { "value": 0, "endTime": "2025-01-21T00:00:00.000Z" }, { "value": 0, "endTime": "2025-01-20T00:00:00.000Z" } ] }, "followerCount": { // Daily follower count snapshots (past 60 days) "period": "day", "values": [ { "value": 58, // Total followers at end of this day "endTime": "2025-01-21T00:00:00.000Z" // End of day timestamp }, { "value": 92, "endTime": "2025-01-20T00:00:00.000Z" } ] }, "leadSubmissions": { // Number of leads submitted past 60 days "period": "day", "total": 0, "values": [ { "value": 0, "endTime": "2025-01-21T00:00:00.000Z" }, { "value": 0, "endTime": "2025-01-20T00:00:00.000Z" } ] }, "likeCountTotal": { // Total Likes across past 60 days "period": "day", "total": 1161, "values": [ { "value": 625, // Number of likes received on this date "endTime": "2025-01-21T00:00:00.000Z" // End of the day timestamp }, { "value": 536, "endTime": "2025-01-20T00:00:00.000Z" } ] }, "phoneNumberClicks": { // Number of clicks collected on the Phone Number button past 60 days "period": "day", "total": 0, "values": [ { "value": 0, "endTime": "2025-01-21T00:00:00.000Z" }, { "value": 0, "endTime": "2025-01-20T00:00:00.000Z" } ] }, "profileViews": { // Total profile views past 60 days "period": "day", "total": 713, "values": [ { "value": 360, "endTime": "2025-01-21T00:00:00.000Z" }, { "value": 353, "endTime": "2025-01-20T00:00:00.000Z" } ] }, "shareCountTotal": { // Total share count past 60 days "period": "day", "total": 3, "values": [ { "value": 2, "endTime": "2025-01-21T00:00:00.000Z" }, { "value": 1, "endTime": "2025-01-20T00:00:00.000Z" } ] }, "url": "https://vm.tiktok.com/ZT2NdEX33/", "userImage": "https://p16-pu-sign-useast8.tiktokcdn-us.com/tos-useastc", "username": "ayrshare", "videoCountTotal": 121, // Total video count "viewCountTotal": { // Total view count past 60 days "period": "day", "total": 17808, "values": [ { "value": 9324, "endTime": "2025-01-21T00:00:00.000Z" }, { "value": 8484, "endTime": "2025-01-20T00:00:00.000Z" } ] } }, "lastUpdated": "2025-01-24T23:33:12.678Z", "nextUpdate": "2025-01-24T23:44:12.678Z" }, "youtube": { // If no analytics are available for a given day, not values will be returned "analytics": { "averageViewDuration": { "period": "day", "total": 4, "values": [ { "value": 4, "endTime": "2024-03-18T00:00:00.000Z" } ] }, "averageViewPercentage": { "period": "day", "total": 61.99, "values": [ { "value": 61.99, "endTime": "2024-03-18T00:00:00.000Z" } ] }, "comments": { "period": "day", "total": 0, "values": [ { "value": 0, "endTime": "2024-03-18T00:00:00.000Z" } ] }, "created": "2010-01-10T15:30:57Z", "description": "The best place to find the best products", "dislikes": { "period": "day", "total": 0, "values": [ { "value": 0, "endTime": "2024-03-18T00:00:00.000Z" } ] }, "estimatedMinutesWatched": { "period": "day", "total": 23.12, "values": [ { "value": 23.12, "endTime": "2024-03-18T00:00:00.000Z" } ] }, "handle": "@helmar1066", "hiddenSubscriberCount": false, "id": "UCsp6CnxiNbUU0AJtuYKZwXQ", "isChannelMonetizationEnabled": false, "isLinked": true, "likes": { "period": "day", "total": 12, "values": [ { "value": 12, "endTime": "2024-03-18T00:00:00.000Z" } ] }, "longUploadsStatus": "allowed", "playlistId": "UUsp6CnxiNbUU0AJtuYKZwXQ", "playlists": [ { "description": "", "id": "PLcLpmVOb3fDaf7Mps-5GnCxRZy_FBBEv4", "publishedAt": "2022-10-21T21:05:55Z", "title": "Super One", "url": "https://www.youtube.com/playlist?list=PLcLpmVOb3fDaf7Mps-5GnCxRZy_FBBEv4", "channelId": "UCsp6CnxiNbUU0AJtuYKZwXQ" }, { "description": "", "id": "FLsp6CnxiNbUU0AJtuYKZwXQ", "publishedAt": "2012-02-26T19:16:55Z", "title": "Favorites", "url": "https://www.youtube.com/playlist?list=FLsp6CnxiNbUU0AJtuYKZwXQ", "channelId": "UCsp6CnxiNbUU0AJtuYKZwXQ" } ], "privacyStatus": "public", "shares": { "period": "day", "total": 3, "values": [ { "value": 3, "endTime": "2024-03-18T00:00:00.000Z" } ] }, "subscriberCount": 0, "subscribersGained": { "period": "day", "total":80, "values": [ { "value": 8, "endTime": "2024-03-18T00:00:00.000Z" } ] }, "subscribersLost": { "period": "day", "total": 1, "values": [ { "value": 1, "endTime": "2024-03-18T00:00:00.000Z" } ] }, "thumbnailUrl": "https://yt3.ggpht.com/ytc/AIdro_lIMzJl5UC7MBEnCzuMpXzai_EvIk5Xh3ErDJ_VTBk=s88-c-k-c0x00ffffff-no-rj", "title": "helmar1066", "url": "https://www.youtube.com/@helmar1066", "videoCount": 5, "videosAddedToPlaylists": { "period": "day", "total": 2, "values": [ { "value": 2, "endTime": "2024-03-18T00:00:00.000Z" } ] }, "videosRemovedFromPlaylists": { "period": "day", "total": 1, "values": [ { "value": 1, "endTime": "2024-03-18T00:00:00.000Z" } ] }, "viewCount": 7, "views": { "period": "day", "total": 3, "values": [ { "value": 3, "endTime": "2024-03-18T00:00:00.000Z" } ] } }, "quarters": 1, "lastUpdated": "2024-04-25T21:24:45.433Z", "nextUpdate": "2024-04-25T21:35:45.433Z" } "status": "success" } ``` ```json 200: Backfilled Response theme={"system"} { "status": "success", "twitter": { "analytics": { "created": "2025-12-19T14:09:06Z", "description": "", "displayName": "Andrew Williams", "followersCount": 1523, "followingCount": 412, "tweetCount": 8721, "likeCount": 3200, "listedCount": 5, "username": "andrewwil1985", "backfilledFrom": "2026-04-08T14:30:00.000Z" }, "lastUpdated": "2026-04-09T10:15:00.000Z", "nextUpdate": "2026-04-09T10:26:00.000Z" } } ``` ```json 200: Recovered Response theme={"system"} { "status": "success", "twitter": { "analytics": { "created": "2025-12-19T14:09:06Z", "description": "", "displayName": "Andrew Williams", "followersCount": 1523, "followingCount": 412, "tweetCount": 8721, "likeCount": 3200, "listedCount": 5, "username": "andrewwil1985", "recoveredFrom": "2026-04-08T14:30:00.000Z" }, "lastUpdated": "2026-04-09T10:15:00.000Z", "nextUpdate": "2026-04-09T10:26:00.000Z" } } ``` ```json 400: Bad Request theme={"system"} { "action": "request", "status": "error", "code": 101, "message": "Missing or incorrect parameters. Please verify with the docs. /rest-api/endpoints Details: One of the platforms requested in not supported." } ``` ```json 400: Social Not Linked theme={"system"} { "linkedin": { "action": "post", "status": "error", "code": 156, "message": "LinkedIn is not properly linked and must be re-linked. Please go to the Social Accounts ppage and re-link LinkedIn." }, "code": 156, "status": "error" } ``` ```json 400: Partial Error theme={"system"} { // A partial error response is returned if some of the data is available but there is an error. "youtube": { "views": 401, "comments": 2, "likes": 37, "dislikes": 2, "estimatedMinutesWatched": 102, "averageViewDuration": 12, "averageViewPercentage": 32.66, "subscribersGained": 2, "subscribersLost": 4, "videosAddedToPlaylists": 2, "videosRemovedFromPlaylists": 1, "shares": 25, "errors": [ { "action": "authorization", "status": "error", "code": 193, "message": "An error occurred connecting to YouTube. Please try your request once more or re-linking YouTube on the social linking page if the issue persists: https://www.ayrshare.com/docs/dashboard/connect-social-accounts/youtube", "details": "Error getting YouTube channel and playlist analytics: Insufficient Permission" } ], "status": "error" }, "lastUpdated": "2025-01-27T02:59:13.119Z", "nextUpdate": "2025-01-27T03:10:13.119Z", "status": "error" } ``` # Analytics on a Post by Social ID Source: https://www.ayrshare.com/docs/apis/analytics/social-by-id POST /analytics/post Get analytics for posts that did not originate via Ayrshare by sending the Social Post ID to the analytics endpoint with searchPlatformId set to true. > [!NOTE] > > **Targeting X/Twitter?** Starting March 31, 2026, all X operations require your own API credentials. After linking X via OAuth, include these 2 headers in your request: > > `X-Twitter-OAuth1-Api-Key` — Your API Key (Consumer Key) > > `X-Twitter-OAuth1-Api-Secret` — Your API Key Secret (Consumer Secret) > > **One-time setup per Ayrshare account.** You create one X Developer App and reuse the same API Key and Secret across every sub-profile / end-user you link. You do *not* create a new app per customer. > > Not linked yet? See the [full setup guide](/docs/dashboard/connect-social-accounts/x-twitter-byo-keys) to connect your X account. > > Your keys are never logged or stored by Ayrshare. > [!WARNING] > > `/apis/analytics/social-by-id` is this documentation page, not an API route. Send both single-post and batch requests to `POST /analytics/post` with exactly one `platforms` value and `searchPlatformId: true`: `{"id":"SOCIAL_POST_ID","platforms":["twitter"],"searchPlatformId":true}` for one post, or `{"postIds":["ID_1","ID_2"],"platforms":["twitter"],"searchPlatformId":true}` for up to 100 posts. Retrieve analytics for posts that did not originate via Ayrshare by providing the low-level [Social Post ID](/docs/apis/overview#social-post-id). This ID is returned in the `postIds` field of the [/post endpoint](/docs/apis/post/post). The linked account must be the owner of the post to retrieve the analytics (exception: YouTube; see below). Support platforms: `Bluesky`, `Facebook`, `Instagram`, `LinkedIn`, `Pinterest`, `Reddit`, `Snapchat`, `Threads`, `TikTok`, `Twitter`, and `YouTube`.
    • The call is the same as the [Analytics on a Post endpoint](/docs/apis/analytics/post). The key difference is you use the post id return by the social network instead of the Ayrshare ID. Also include the `searchPlatformId: true` parameter to notify the endpoint you're searching by the Social Post ID.
    • Use the [Get All Post History endpoint](/docs/apis/history/overview) to retrieve posts and IDs originating outside of Ayrshare found in the `id` field.
    • Recommend to only use for posts not sent via Ayrshare. For posts sent via Ayrshare, use the [analytics endpoint](/docs/apis/analytics/post).
    • Analytics on Instagram posts that were published before the user's account was converted to a business account from a personal account have limited analytics.
    • If retrieving YouTube analytics for a post that doesn't belong to your channel using the social ID method, the API will return descriptive metadata about the content while showing zeros for all numerical metrics. Descriptive information such as title, description, tags, channel title, privacy status, and thumbnail URLs will be correctly populated, providing context about the video content. However, all numerical performance metrics including views, likes, comments, shares, subscriber changes, watch time, and playlist additions will return as zero values.
    • When using `postIds` for X/Twitter threads, each tweet's Social Post ID must be provided individually, thread tweets are not automatically included when querying the parent post.
    ## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` **Profile-Key** `string` [Profile Key](/docs/apis/overview#profile-key-format) of a User Profile. Format: `Profile-Key: PROFILE_KEY` ## Body Parameters > [!NOTE] > > Either `id` or `postIds` must be provided. Use `id` for a single post or `postIds` for retrieving analytics on multiple posts in a single request. **id** `string` [Social Post ID](/docs/apis/overview#social-post-id) returned from the [/post endpoint](/docs/apis/post/post). This is the `id` field for an individual social network found in the `postIds` array. Either `id` or `postIds` is required. **postIds** `array` Array of [Social Post IDs](/docs/apis/overview#social-post-id) to retrieve analytics for multiple posts in a single request. Maximum of 100 IDs allowed (25 for Snapchat). Either `id` or `postIds` is required. ```json theme={"system"} { "postIds": ["1979851549871354062", "2011793803951137234"] } ``` **platforms** `array` *required* String array of platforms to retrieve analytics. Only one value is allowed. Available values: ```json theme={"system"} { "platforms": ["bluesky", "facebook", "instagram", "linkedin", "pinterest", "reddit", "snapchat", "threads", "tiktok", "twitter", "youtube"] } ``` **searchPlatformId** `boolean` *required* (default: false) Set to `true` to search by the Social Post ID. ```json Single Post theme={"system"} { // Facebook Social Post ID "id": "104923907983682_108329000309742", "platforms": [ // Select only one platform at a time: // facebook, instagram, youtube, threads, tiktok, or twitter "facebook" ], "searchPlatformId": true // Required } ``` ```json Multiple Posts theme={"system"} { // Array of Social Post IDs (max 100) "postIds": ["1979851549871354062", "2011793803951137234"], "platforms": [ // Select only one platform at a time: // facebook, instagram, youtube, threads, tiktok, or twitter "twitter" ], "searchPlatformId": true // Required } ``` > [!NOTE] > > When cumulative metrics (e.g., likes, comments, views) are temporarily unavailable from the social network, the API automatically backfills them from stored data. Two optional fields may appear in the per-platform `analytics` object: > > * **`backfilledFrom`** (string, ISO 8601) — Present when one or more cumulative metrics were substituted from stored data. The timestamp indicates when the stored data was last updated. > * **`recoveredFrom`** (string, ISO 8601) — Present when the entire analytics response was recovered from stored data due to a complete API failure. The timestamp indicates when the stored data was last updated. For Snapchat, where `analytics` is a list, `recoveredFrom` appears beside `analytics` instead. > > Stored data older than 4 days is considered stale and will not be used for backfill or recovery. ```json 200: Single Post Response theme={"system"} { /** The response is the same as Analytics on a Post. Please see that endpoint for details. Note: Some metrics are not available for posts not owned by the authorized account. For example: X does not return non-public metrics or organic metrics for Tweets not sent by the authorized user. */ } ``` ```json 200: Multiple Posts Response theme={"system"} { "twitter": [ { "id": "1313589441919827982", // Twitter Social Post ID "postUrl": "https://www.twitter.com/myaccount/1313589441919827982", "analytics": { "created": "2022-09-07T22:12:10.000Z", "entities": { "urls": [ { "start": 77, "end": 96, "url": "https://t.co/abc123", "expandedUrl": "https://www.ayrshare.com/docs", "displayUrl": "ayrshare.com/docs", "unwoundUrl": "https://www.ayrshare.com/docs" } ], "hashtags": [ { "start": 97, "end": 112, "tag": "SocialMediaAPI" } ], "mentions": [ { "start": 113, "end": 122, "username": "ayrshare" } ] }, "name": "Wonder World", "post": "Just launched our social media campaign using Ayrshare! Check out the API at https://t.co/abc123 #SocialMediaAPI @ayrshare", "publicMetrics": { "retweetCount": 0, "quoteCount": 0, "likeCount": 0, "replyCount": 0, "bookmarkCount": 0, "impressionCount": 23 }, "nonPublicMetrics": { "userProfileClicks": 1, "engagements": 1, "impressionCount": 5 }, "organicMetrics": { "likeCount": 0, "impressionCount": 5, "replyCount": 0, "retweetCount": 0, "userProfileClicks": 1 }, "username": "wondrous" }, "lastUpdated": "2022-04-23T18:44:29.778Z", "nextUpdate": "2022-04-23T19:19:29.778Z" }, { "id": "1313589441919827999", // Twitter Social Post ID "postUrl": "https://www.twitter.com/myaccount/1313589441919827999", "analytics": { "created": "2022-09-08T14:30:00.000Z", "name": "Wonder World", "post": "Another great day for social media automation!", "publicMetrics": { "retweetCount": 5, "quoteCount": 2, "likeCount": 15, "replyCount": 3, "bookmarkCount": 1, "impressionCount": 150 }, "nonPublicMetrics": { "userProfileClicks": 8, "engagements": 12, "impressionCount": 145 }, "organicMetrics": { "likeCount": 15, "impressionCount": 145, "replyCount": 3, "retweetCount": 5, "userProfileClicks": 8 }, "username": "wondrous" }, "lastUpdated": "2022-04-23T18:44:29.778Z", "nextUpdate": "2022-04-23T19:19:29.778Z" } ], "status": "success", "code": 200 } ``` ```json 200: Backfilled Response theme={"system"} { "twitter": [ { "id": "1313589441919827982", "postUrl": "https://www.twitter.com/myaccount/1313589441919827982", "analytics": { "created": "2022-09-07T22:12:10.000Z", "name": "Wonder World", "post": "Just launched our social media campaign!", "publicMetrics": { "retweetCount": 5, "quoteCount": 2, "likeCount": 15, "replyCount": 3, "bookmarkCount": 1, "impressionCount": 150 }, "username": "wondrous", "backfilledFrom": "2026-04-08T14:30:00.000Z" }, "lastUpdated": "2026-04-09T10:15:00.000Z", "nextUpdate": "2026-04-09T10:26:00.000Z" } ], "status": "success", "code": 200 } ``` ```json 200: Recovered Response theme={"system"} { "twitter": [ { "id": "1313589441919827982", "postUrl": "https://www.twitter.com/myaccount/1313589441919827982", "analytics": { "created": "2022-09-07T22:12:10.000Z", "name": "Wonder World", "post": "Just launched our social media campaign!", "publicMetrics": { "retweetCount": 5, "quoteCount": 2, "likeCount": 15, "replyCount": 3, "bookmarkCount": 1, "impressionCount": 150 }, "username": "wondrous", "recoveredFrom": "2026-04-08T14:30:00.000Z" }, "lastUpdated": "2026-04-09T10:15:00.000Z", "nextUpdate": "2026-04-09T10:26:00.000Z" } ], "status": "success", "code": 200 } ``` ```json 400: Bad Request theme={"system"} { "lastUpdated": "2023-12-08T03:40:31.185Z", "nextUpdate": "2023-12-08T03:51:31.185Z", "youtube": { "action": "post", "status": "error", "code": 186, "message": "Post ID not found. Please verify the top level ID returned from the /post endpoint is being sent, the Profile Key is included if applicable, and the post at the social network has not been deleted. If you are trying to retrieve a post originating outside of Ayrshare, please use the https://www.ayrshare.com/docs/apis/analytics/social-by-id", "id": "dtuu-jDp4381" } } ``` > [!NOTE] > > A `code: 186` response means the Social Post ID was not found for the selected account and platform. The URL in the message links back to this documentation page; it does not identify another API endpoint. Correct the request and retry `POST /analytics/post` using one of the request shapes above. # YouTube Playlists Source: https://www.ayrshare.com/docs/apis/analytics/youtube-playlists GET /analytics/youTubePlaylists Get the playlists for the connected YouTube channel, with each playlist's views, playlist starts, estimated minutes watched, and average view duration. ## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` **Profile-Key** `string` [Profile Key](/docs/apis/overview#profile-key-format) of a User Profile. Format: `Profile-Key: PROFILE_KEY` ```bash cURL theme={"system"} curl \ -H "Authorization: Bearer API_KEY" \ -X GET https://api.ayrshare.com/api/analytics/youTubePlaylists ``` ```javascript JavaScript theme={"system"} const API_KEY = "API_KEY"; fetch("https://api.ayrshare.com/api/analytics/youTubePlaylists", { method: "GET", headers: { Authorization: `Bearer ${API_KEY}`, }, }) .then((res) => res.json()) .then((json) => console.log(json)) .catch(console.error); ``` ```python Python theme={"system"} import requests headers = {'Authorization': 'Bearer API_KEY'} r = requests.get('https://api.ayrshare.com/api/analytics/youTubePlaylists', headers=headers) print(r.json()) ``` ```php PHP theme={"system"} true, CURLOPT_HTTPHEADER => $headers ]); $response = curl_exec($curl); if ($response === false) { echo 'Curl error: ' . curl_error($curl); } else { echo json_encode(json_decode($response), JSON_PRETTY_PRINT); } curl_close($curl); ``` ```csharp C# theme={"system"} using System; using System.Net.Http; using System.Threading.Tasks; namespace YouTubePlaylistsGETRequest_csharp { class YouTubePlaylists { private static readonly HttpClient client = new HttpClient(); static async Task Main(string[] args) { string API_KEY = "API_KEY"; string url = "https://api.ayrshare.com/api/analytics/youTubePlaylists"; client.DefaultRequestHeaders.Add("Authorization", "Bearer " + API_KEY); try { HttpResponseMessage response = await client.GetAsync(url); response.EnsureSuccessStatusCode(); string responseBody = await response.Content.ReadAsStringAsync(); Console.WriteLine(responseBody); } catch (HttpRequestException e) { Console.WriteLine($"Error: {e.Message}"); } } } } ``` ```json 200: Success theme={"system"} { "status": "success", "channelId": "UCsp6CnxiNbUU0AJtuYKZw", "playLists": [ { "averageTimeInPlaylist": 2.2, "averageViewDuration": 3.1, "description": "A great list", "estimatedMinutesWatched": 21, "estimatedRedMinutesWatched": 23, "id": "PLcLpmVOb3fDaf7Mps", "playlistStarts": 23, "thumbNail": { "url": "https://i.ytimg.com/vi/default.jpg", "width": 120, "height": 90 }, "title": "Super Playlist", "views": 55, "viewsPerPlaylistStart": 32 }, { "averageTimeInPlaylist": 2, "averageViewDuration": 4.5, "description": "A great list 2", "estimatedMinutesWatched": 2, "estimatedRedMinutesWatched": 3, "id": "FLsp6CnxiNbUU0AJtu", "playlistStarts": 23, "thumbNail": { "url": "https://i.ytimg.com/vi/default.jpg", "width": 120, "height": 90 }, "title": "Favorites", "views": 20, "viewsPerPlaylistStart": 23 } ], "lastUpdated": "2023-01-18T03:09:59.757Z", "nextUpdate": "2023-01-18T03:42:29.757Z" } ``` ```json 400: Error getting analytics theme={"system"} { "action": "analytics", "status": "error", "code": 294, "message": "Error getting analytics." } ``` # Delete Auto Schedule Source: https://www.ayrshare.com/docs/apis/auto-schedule/delete-schedule DELETE /auto-schedule/delete Delete an auto schedule by its title, or the default schedule if no title is given, and optionally reset the schedule's starting point to the current time. Delete a particular auto schedule. Provide the title of the schedule or "default" is used. ## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` **Profile-Key** `string` [Profile Key](/docs/apis/overview#profile-key-format) of a User Profile. Format: `Profile-Key: PROFILE_KEY` ## Body Parameters **title** `string` (default: default) The title of the schedule to delete. This should match a schedule title that was previously created using the [Set Auto Schedule endpoint](/docs/apis/auto-schedule/set-schedule). If no title is provided, the system will attempt to delete the "default" schedule. **deleteLastScheduleDate** `boolean` (default: false) Reset the schedule's starting point to the current time. When set to `true`, this will clear the last used schedule date, causing any new posts to be scheduled starting from the current time. Any posts that were already scheduled will remain unchanged and publish at their original times. ```javascript cURL theme={"system"} curl \ -H "Authorization: Bearer API Key" \ -H 'Content-Type: application/json' \ -d '{"title": "Schedule Title"}' \ -X DELETE https://api.ayrshare.com/api/auto-schedule/delete ``` ```javascript JavaScript theme={"system"} const API_KEY = "API_KEY"; const title = "Schedule Title"; fetch("https://api.ayrshare.com/api/auto-schedule/delete", { method: "DELETE", headers: { "Content-Type": "application/json", "Authorization": `Bearer ${API_KEY}` }, body: JSON.stringify({ title }), }) .then((res) => res.json()) .then((json) => console.log(json)) .catch(console.error); ``` ```python Python theme={"system"} import requests payload = {'title': 'Schedule Title'} headers = {'Content-Type': 'application/json', 'Authorization': 'Bearer API_KEY'} r = requests.delete('https://api.ayrshare.com/api/auto-schedule/delete', json=payload, headers=headers) print(r.json()) ``` ```php PHP theme={"system"} $title ]; $ch = curl_init(); curl_setopt_array($ch, [ CURLOPT_URL => "https://api.ayrshare.com/api/auto-schedule/delete", CURLOPT_RETURNTRANSFER => true, CURLOPT_CUSTOMREQUEST => "DELETE", // Set the request method to DELETE CURLOPT_POSTFIELDS => json_encode($data), CURLOPT_HTTPHEADER => [ "Content-Type: application/json", "Authorization: Bearer " . $API_KEY ] ]); $response = curl_exec($ch); if (curl_errno($ch)) { echo 'Error: ' . curl_error($ch); } else { $json = json_decode($response, true); print_r($json); } curl_close($ch); ``` ```csharp C# theme={"system"} using System; using System.Net.Http; using System.Text; using System.Threading.Tasks; namespace AutoScheduleDELETERequest_csharp { class AutoScheduleDELETE { private static readonly HttpClient client = new HttpClient(); static async Task Main(string[] args) { string API_KEY = "API_KEY"; string url = "https://api.ayrshare.com/api/auto-schedule/delete"; client.DefaultRequestHeaders.Add("Authorization", "Bearer " + API_KEY); string json = "{\"title\": \"Schedule Title\"}"; try { var request = new HttpRequestMessage { Method = HttpMethod.Delete, RequestUri = new Uri(url), Content = new StringContent(json, Encoding.UTF8, "application/json") }; HttpResponseMessage response = await client.SendAsync(request); response.EnsureSuccessStatusCode(); string responseBody = await response.Content.ReadAsStringAsync(); Console.WriteLine(responseBody); } catch (HttpRequestException e) { Console.WriteLine($"Error: {e.Message}"); } } } } ``` ```javascript 200: Success theme={"system"} { status: "success", title: "Schedule Title" } ``` # List Auto Schedule Source: https://www.ayrshare.com/docs/apis/auto-schedule/list-schedule GET /auto-schedule/list List your active auto schedules, including each schedule's title, times, and last scheduled date, which shows the next or most recent schedule date. List the active auto schedules. Returns an array of schedules with titles, times, and last scheduled date. The `lastScheduleDate` timestamp is the next schedule date or the previously scheduled date if no pending posts. ## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` **Profile-Key** `string` [Profile Key](/docs/apis/overview#profile-key-format) of a User Profile. Format: `Profile-Key: PROFILE_KEY` ```javascript cURL theme={"system"} curl \ -H "Authorization: Bearer API_KEY" \ -X GET https://api.ayrshare.com/api/auto-schedule/list ``` ```javascript JavaScript theme={"system"} const API_KEY = "API_KEY"; fetch("https://api.ayrshare.com/api/auto-schedule/list", { method: "GET", headers: { "Authorization": `Bearer ${API_KEY}` } }) .then((res) => res.json()) .then((json) => console.log(json)) .catch(console.error); ``` ```python Python theme={"system"} import requests headers = {'Authorization': 'Bearer API_KEY'} r = requests.get('https://api.ayrshare.com/api/auto-schedule/list', headers=headers) print(r.json()) ``` ```php PHP theme={"system"} "https://api.ayrshare.com/api/auto-schedule/list", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . $API_KEY ] ]); $response = curl_exec($ch); if (curl_errno($ch)) { echo 'Error: ' . curl_error($ch); } else { $json = json_decode($response, true); print_r($json); } curl_close($ch); ``` ```csharp C# theme={"system"} using System; using System.Net.Http; using System.Threading.Tasks; namespace AutoScheduleGETRequest_csharp { class AutoSchedule { private static readonly HttpClient client = new HttpClient(); static async Task Main(string[] args) { string API_KEY = "API_KEY"; string url = "https://api.ayrshare.com/api/auto-schedule/list"; client.DefaultRequestHeaders.Add("Authorization", "Bearer " + API_KEY); try { HttpResponseMessage response = await client.GetAsync(url); response.EnsureSuccessStatusCode(); string responseBody = await response.Content.ReadAsStringAsync(); Console.WriteLine(responseBody); } catch (HttpRequestException e) { Console.WriteLine($"Error: {e.Message}"); } } } } ``` ```javascript 200: Success theme={"system"} { "status": "success", "schedules": { "Title 1": { "lastScheduleDate": "2024-01-05T22:30:00Z", "schedule": [ "20:03Z", "22:34Z" ] }, "Title 2": { "schedule": [ "13:05Z", "22:14Z" ], "lastScheduleDate": "2024-01-04T22:30:00Z", "daysOfWeek": [ 2, 3, 4 ] } } } ``` # Auto Schedule Overview Source: https://www.ayrshare.com/docs/apis/auto-schedule/overview Auto schedule lets you create posting schedules with times on chosen days and queue posts into the next available time slot via the Publish Post endpoint. Auto scheduling allows you to create pre-defined posting schedules. You can set up multiple schedules, each with specific posting times on chosen days. Auto schedule is distinct from the `scheduleDate` field in the [Publish Post endpoint](/docs/apis/post/post). ## Key Features of Auto Schedule
    • Create custom schedules, e.g., post at 9 AM, 2 PM, and 5 PM on Mondays and Wednesdays.
    • Posts are automatically queued in the next available time slot.
    • No limit on the number of schedules you can create.
    • Auto schedule is distinct from the `scheduleDate` field in the [Publish Post endpoint](/docs/apis/post/post) and it is meant to automate scheduling. You should use either auto schedule or `scheduleDate`, but not both.
    ## Example Auto Schedule Scenario 1. Current time: Monday, 10 AM (GMT). 2. You create an auto schedule for posting at 9 AM and 5 PM. 3. You add three posts to this schedule. 4. The system automatically schedules these posts for: Monday at 5 PM, Tuesday at 9 AM, and Tuesday at 5 PM. > [!NOTE] > > Deleting a scheduled post doesn't free up its time slot. For example, if you delete the Monday 5 > PM post and add a new post to the same schedule, it will be placed in the next available slot (in > this case, Wednesday at 9 AM). ## Publish Using Auto Schedule The schedule may be applied with the [Publish Post endpoint](/docs/apis/post/post) using the following fields: ```json Auto Schedule theme={"system"} "autoSchedule": { "schedule": true, // required "title": "Schedule Title" // optional - case-sensitive } ```
    • `schedule`: (required) A Boolean value that must be set to `true` to enable auto scheduling.
    • `title`: (optional) A String value that specifies the name of the schedule. This `title` should match the name of a schedule created using the [Set Auto Schedule](/docs/apis/auto-schedule/overview) endpoint. If not provided, the default title is `default`. Note that the `title` is case-sensitive and must be an exact match.
    See your list of auto schedules by calling the [List Auto Schedules](/docs/apis/auto-schedule/list-schedule) endpoint. Please see this article on [using the auto scheduling API](https://www.ayrshare.com/blog/understanding-the-ayrshare-auto-schedule-api-endpoint/) for more information. ## User Profile Auto Schedule Auto schedule for a particular user profile by [adding the PROFILE\_KEY in the header](/docs/apis/overview#profile-key-format). # Get Pending Auto Schedule Posts Source: https://www.ayrshare.com/docs/apis/auto-schedule/pending-auto-schedule GET /auto-schedule/pending Retrieve every auto schedule post that is still pending and not yet published, using a GET request to /auto-schedule/pending with your API key header. Retrieve all the pending (not yet published) auto schedule posts. ## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` **Profile-Key** `string` [Profile Key](/docs/apis/overview#profile-key-format) of a User Profile. Format: `Profile-Key: PROFILE_KEY` ```javascript cURL theme={"system"} curl \ -H "Authorization: Bearer API_KEY" \ -X GET https://api.ayrshare.com/api/auto-schedule/pending ``` ```javascript JavaScript theme={"system"} const API_KEY = "API_KEY"; fetch("https://api.ayrshare.com/api/auto-schedule/pending", { method: "GET", headers: { "Authorization": `Bearer ${API_KEY}` } }) .then((res) => res.json()) .then((json) => console.log(json)) .catch(console.error); ``` ```python Python theme={"system"} import requests headers = {'Authorization': 'Bearer API_KEY'} r = requests.get('https://api.ayrshare.com/api/auto-schedule/pending', headers=headers) print(r.json()) ``` ```php PHP theme={"system"} "https://api.ayrshare.com/api/auto-schedule/pending", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . $API_KEY ] ]); $response = curl_exec($ch); if (curl_errno($ch)) { echo 'Error: ' . curl_error($ch); } else { $json = json_decode($response, true); print_r($json); } curl_close($ch); ``` ```csharp C# theme={"system"} using System; using System.Net.Http; using System.Threading.Tasks; namespace AutoSchedulePendingGETRequest_csharp { class AutoSchedulePending { private static readonly HttpClient client = new HttpClient(); static async Task Main(string[] args) { string API_KEY = "API_KEY"; string url = "https://api.ayrshare.com/api/auto-schedule/pending"; client.DefaultRequestHeaders.Add("Authorization", "Bearer " + API_KEY); try { HttpResponseMessage response = await client.GetAsync(url); response.EnsureSuccessStatusCode(); string responseBody = await response.Content.ReadAsStringAsync(); Console.WriteLine(responseBody); } catch (HttpRequestException e) { Console.WriteLine($"Error: {e.Message}"); } } } } ``` ```json 200: Pending Posts theme={"system"} { "status": "success", "posts": [ { "id": "Ut1fWU6XkqkMayHGnJZ", "post": "Today is a great day!", "platforms": ["facebook", "instagram"], "status": "pending", "autoScheduled": true, "scheduleDate": "2026-11-02T14:00:00Z", "created": "2026-10-27T09:12:44Z" } ] } ``` # Set Auto Schedule Source: https://www.ayrshare.com/docs/apis/auto-schedule/set-schedule POST /auto-schedule/set Set up an auto-post schedule with UTC times, a title, a start date, days of the week, and excluded dates so each post is sent at the next available time. Set up an auto-post schedule by providing times to send. Post will automatically be sent at the next available time. If no more times are available today, the first available time tomorrow will be used, and so on. > [!NOTE] > > If you're looking to just schedule a post for a future date, please see the `scheduleDate` > parameter of [/post](/docs/apis/post/post) Use the auto-schedule by setting the /post autoSchedule parameter to `true` and the `title` if you want to use a particular schedule. Example, set the times to UTC time 13:05Z and 20:14Z and `autoSchedule: true` in the post. The post will be scheduled at the next available time of 13:05Z or 20:14Z. ## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` **Profile-Key** `string` [Profile Key](/docs/apis/overview#profile-key-format) of a User Profile. Format: `Profile-Key: PROFILE_KEY` ## Body Parameters **schedule** `array` *required* Array of strings of scheduled times to auto-post. The array will be treated as a set, so duplicates removed. Format: ISO-8601 UTC. Example: `["13:05Z", "22:14Z"]`. Not required if `setStartDate` provided. **title** `string` (default: default) You can create multiple different posting schedules by assigning a unique title to each one. The title must contain only alphanumeric characters - special characters like `*`, `~`, `/`, `[`, or `]` are not allowed. If you specify the `title` in /post with autoScheduleTitle, that schedule will be used. **setStartDate** `string` Set a specific beginning date to start the auto schedule, provide a ISO-8601 UTC date time. E.g. `2021-07-08T12:30:00Z`. The start time will be applied to the provided "title" or will use the default title if one isn't provided. New posts will go out from the start date onwards. Previously scheduled posts are not affected. **daysOfWeek** `array` Specify which days of the week the post should be sent. Values 0-6 (Sunday - Saturday). For example `[1, 3]` will only publish posts on Mondays and Wednesdays. **excludeDates** `array` Exclude certain dates from auto scheduling occurring. For example `["2026-01-01"]` to exclude New Years. Note, only posts auto scheduled after the `excludeDates` has been set will be excluded. ```javascript cURL theme={"system"} curl \ -H "Authorization: Bearer API_KEY" \ -H 'Content-Type: application/json' \ -d '{"schedule": ["13:05Z", "20:14Z"], "title": "Instagram Schedule"}' \ -X POST https://api.ayrshare.com/api/auto-schedule/set ``` ```javascript JavaScript theme={"system"} const API_KEY = "API_KEY"; const title = "Instagram Schedule"; fetch("https://api.ayrshare.com/api/auto-schedule/set", { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${API_KEY}` }, body: JSON.stringify({ schedule: ["13:05Z", "20:14Z"], // required title: title // optional }) }) .then((res) => res.json()) .then((json) => console.log(json)) .catch(console.error); ``` ```php PHP theme={"system"} ["13:05Z", "20:14Z"], // required 'title' => "Instagram Schedule" // optional ]; $ch = curl_init(); curl_setopt_array($ch, [ CURLOPT_URL => "https://api.ayrshare.com/api/auto-schedule/set", CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_POSTFIELDS => json_encode($data), CURLOPT_HTTPHEADER => [ "Content-Type: application/json", "Authorization: Bearer " . $API_KEY ] ]); $response = curl_exec($ch); if (curl_errno($ch)) { echo 'Error: ' . curl_error($ch); } else { $json = json_decode($response, true); print_r($json); } curl_close($ch); ``` ```csharp C# theme={"system"} using System; using System.Net.Http; using System.Threading.Tasks; namespace AutoSchedulePOSTRequest_csharp { class AutoSchedule { private static readonly HttpClient client = new HttpClient(); static async Task Main(string[] args) { string API_KEY = "API_KEY"; string url = "https://api.ayrshare.com/api/auto-schedule/set"; client.DefaultRequestHeaders.Add("Authorization", "Bearer " + API_KEY); var payload = new { schedule = new[] { "13:05Z", "20:14Z" }, title = "Instagram Schedule" }; try { var content = new StringContent( System.Text.Json.JsonSerializer.Serialize(payload), System.Text.Encoding.UTF8, "application/json" ); HttpResponseMessage response = await client.PostAsync(url, content); response.EnsureSuccessStatusCode(); string responseBody = await response.Content.ReadAsStringAsync(); Console.WriteLine(responseBody); } catch (HttpRequestException e) { Console.WriteLine($"Error: {e.Message}"); } } } } ``` ```go Go theme={"system"} package main import ( "bytes" "encoding/json" "log" "net/http" ) func main() { message := map[string]interface{}{ "schedule": []string{"13:05Z", "20:14Z"}, "title": "Instagram Schedule" } bytesRepresentation, err := json.Marshal(message) if err != nil { log.Fatalln(err) } req, _ := http.NewRequest("POST", "https://api.ayrshare.com/api/auto-schedule/set", bytes.NewBuffer(bytesRepresentation)) req.Header.Add("Content-Type", "application/json; charset=UTF-8") req.Header.Add("Authorization", "Bearer API_KEY") res, err := http.DefaultClient.Do(req) if err != nil { log.Fatal("Error:", err) } res.Body.Close() } ``` ```javascript 200: Schedule Set theme={"system"} { status: "success", message: "Auto schedule set.", title: "Instagram Schedule", } ``` # Create Automation Source: https://www.ayrshare.com/docs/apis/automations/create-automation POST /automations Create a new automation for an Instagram account or Facebook Page that fires one or more actions, such as send_dm, when any of its triggers matches. > [!NOTE] > > **Beta.** See the [Automations Overview](/docs/apis/automations/overview) for the full feature description, including the Beta status. Create a new automation that fires one or more actions when any of its triggers matches. The automation activates immediately. See the [Overview](/docs/apis/automations/overview) for the full catalog of triggers, actions, template variables, and limits. The connected Instagram account or Facebook Page is derived server-side from the caller's linked profile — **do not pass `accountId`** in the body; the request will be rejected as a validation error if you do. Pass a `profileKey` header to operate under a child profile. ## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` **Profile-Key** `string` [Profile Key](/docs/apis/overview#profile-key-format) of a User Profile. Format: `Profile-Key: PROFILE_KEY` ## Body Parameters **platform** `string` *required* Target platform: `instagram` or `facebook` (the linked Facebook Page). Facebook supports the `comment_keyword`, `dm_keyword` and `dm_reaction` triggers. See [Facebook Pages](/docs/apis/automations/overview#facebook-pages). **name** `string` *required* Short human-readable name. Used in the dashboard and activity logs. **description** `string` Optional longer description. **triggers** `array` *required* One or more triggers (1–50). Each entry is a discriminated union on `type`. See [Overview / Triggers](/docs/apis/automations/overview#triggers) for the full catalog. ```json comment_keyword trigger theme={"system"} { "type": "comment_keyword", "postId": "17895695668004550", // required — Instagram media id or Facebook post id, or "all" "keywords": ["INFO", "LINK"] // required — ≥1 entry, case-insensitive } ``` ```json comment_keyword trigger — every post (evergreen) theme={"system"} { "type": "comment_keyword", "postId": "all", // fires on any post or reel on the account "keywords": ["INFO", "LINK"] } ``` ```json story_reply trigger theme={"system"} { "type": "story_reply", "storyId": "17900000000000000" // optional — scope to one story, or "all" } ``` ```json dm_reaction trigger theme={"system"} { "type": "dm_reaction", "emoji": "❤️" // optional — scope to one emoji } ``` ```json dm_keyword trigger theme={"system"} { "type": "dm_keyword", "keywords": ["help", "support"] // required — ≥1 entry, case-insensitive } ``` **actions** `array` *required* One or more actions (1–50). Each entry is a discriminated union on `type`. See [Overview / Actions](/docs/apis/automations/overview#actions) for the full catalog. Every action also accepts an optional top-level `dedupWindowMinutes` overriding the default 7-day per-recipient dedup window (`0` to disable, max `525600`). ```json send_dm action theme={"system"} { "type": "send_dm", "message": "Hey {{recipient_username}}, here is the link you wanted: https://example.com" } ``` ```json fire_webhook action theme={"system"} { "type": "fire_webhook" // POSTs to your account-level webhook URL (configured in the dashboard). // See Overview for the JSON payload shape. } ``` ```json send_email action theme={"system"} { "type": "send_email", "to": "alerts@example.com", "subject": "New {{platform}} engagement from @{{recipient_username}}", "message": "

    {{recipient_username}} engaged with: {{comment_text}}

    " } ``` ```json Action with per-action dedup override theme={"system"} { "type": "send_dm", "message": "Thanks {{recipient_username}}!", "dedupWindowMinutes": 1440 // 24h instead of the 7-day default } ``` > [!NOTE] > > **Platform compatibility.** Each trigger and action declares which platforms it supports. The validator rejects the request when an entry's `type` is not supported on the requested `platform`. All triggers support Instagram; `comment_keyword`, `dm_keyword` and `dm_reaction` also support Facebook. `send_dm` supports both, `fire_webhook` and `send_email` are platform-agnostic. ```bash cURL theme={"system"} curl \ -H "Authorization: Bearer API_KEY" \ -H 'Content-Type: application/json' \ -d '{ "platform": "instagram", "name": "Spring promo", "triggers": [ { "type": "comment_keyword", "postId": "17895695668004550", "keywords": ["INFO", "LINK"] } ], "actions": [ { "type": "send_dm", "message": "Hey {{recipient_username}}, here is the link you wanted: https://example.com" } ] }' \ -X POST https://api.ayrshare.com/api/automations ``` ```javascript JavaScript theme={"system"} const API_KEY = "API_KEY"; fetch("https://api.ayrshare.com/api/automations", { method: "POST", headers: { "Authorization": `Bearer ${API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ platform: "instagram", name: "Spring promo", triggers: [ { type: "comment_keyword", postId: "17895695668004550", keywords: ["INFO", "LINK"], }, ], actions: [ { type: "send_dm", message: "Hey {{recipient_username}}, here is the link you wanted: https://example.com", }, ], }), }) .then((res) => res.json()) .then((json) => console.log(json)) .catch(console.error); ``` ```python Python theme={"system"} import requests payload = { "platform": "instagram", "name": "Spring promo", "triggers": [ { "type": "comment_keyword", "postId": "17895695668004550", "keywords": ["INFO", "LINK"], } ], "actions": [ { "type": "send_dm", "message": "Hey {{recipient_username}}, here is the link you wanted: https://example.com", } ], } headers = { "Authorization": "Bearer API_KEY", "Content-Type": "application/json", } r = requests.post( "https://api.ayrshare.com/api/automations", json=payload, headers=headers, ) print(r.json()) ``` ```php PHP theme={"system"} $curl = curl_init(); $data = [ "platform" => "instagram", "name" => "Spring promo", "triggers" => [ [ "type" => "comment_keyword", "postId" => "17895695668004550", "keywords" => ["INFO", "LINK"], ], ], "actions" => [ [ "type" => "send_dm", "message" => "Hey {{recipient_username}}, here is the link you wanted: https://example.com", ], ], ]; curl_setopt_array($curl, [ CURLOPT_URL => "https://api.ayrshare.com/api/automations", CURLOPT_RETURNTRANSFER => true, CURLOPT_CUSTOMREQUEST => "POST", CURLOPT_POSTFIELDS => json_encode($data), CURLOPT_HTTPHEADER => [ "Authorization: Bearer API_KEY", "Content-Type: application/json", ], ]); $response = curl_exec($curl); curl_close($curl); echo $response; ``` ```json 200: Success theme={"system"} { "status": "success", "id": "auto_9xKp2Lm4nQ" } ``` ```json 400: Validation failed — unknown template variable theme={"system"} { "action": "request", "status": "error", "code": 473, "message": "Validation failed. Please see: https://www.ayrshare.com/docs/introduction", "details": { "formErrors": [], "fieldErrors": { "actions": ["message contains unknown template variables"] } } } ``` ```json 400: Validation failed — comment_keyword trigger missing keywords theme={"system"} { "action": "request", "status": "error", "code": 473, "message": "Validation failed. Please see: https://www.ayrshare.com/docs/introduction", "details": { "formErrors": [], "fieldErrors": { "triggers": ["Invalid input: expected array, received undefined"] } } } ``` ```json 400: Validation failed — unrecognized field (e.g. accountId) theme={"system"} { "action": "request", "status": "error", "code": 473, "message": "Validation failed. Please see: https://www.ayrshare.com/docs/introduction", "details": { "formErrors": ["Unrecognized key: \"accountId\""], "fieldErrors": {} } } ``` ```json 403: Plan tier required theme={"system"} { "action": "automation", "status": "error", "code": 468, "message": "DM automations require a Business or Enterprise plan" } ``` ```json 403: Feature flag not yet enabled theme={"system"} { "action": "request", "status": "error", "code": 472, "message": "This feature is not yet available on your account. Please contact us if you'd like early access." } ``` ```json 400: No linked social account for this platform theme={"system"} { "action": "automation", "status": "error", "code": 471, "message": "No social account linked on the requested platform for this profile. Link the account on the Social Accounts page and try again." } ``` ```json 429: Active automation cap reached theme={"system"} { "action": "automation", "status": "error", "code": 470, "message": "Active automation limit reached for your plan tier" } ``` # Delete Automation Source: https://www.ayrshare.com/docs/apis/automations/delete-automation DELETE /automations/:id Soft-delete an automation by ID to stop new dispatches. Activity history stays queryable, it no longer counts against your cap, and it can't be restored. > [!NOTE] > > **Beta.** See the [Automations Overview](/docs/apis/automations/overview) for the full feature description. Soft-deletes the automation. The master row is flagged `deleted: true, active: false`; no new dispatches occur from this point on. Existing triggers, actions, and activity rows are **not** cascaded. The activity history remains queryable via [`GET /automations/:id/activity`](/docs/apis/automations/get-activity) for as long as the rows exist in Firestore (retention is indefinite for trace and analytics). Soft-deleted automations no longer count against your active automation cap. They cannot be restored via the API; create a new automation instead. ## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` **Profile-Key** `string` [Profile Key](/docs/apis/overview#profile-key-format) of a User Profile. Format: `Profile-Key: PROFILE_KEY` ## Path Parameters **id** `string` *required* The automation ID returned from `POST /automations`. ```bash cURL theme={"system"} curl \ -H "Authorization: Bearer API_KEY" \ -X DELETE https://api.ayrshare.com/api/automations/auto_9xKp2Lm4nQ ``` ```javascript JavaScript theme={"system"} const API_KEY = "API_KEY"; const id = "auto_9xKp2Lm4nQ"; fetch(`https://api.ayrshare.com/api/automations/${id}`, { method: "DELETE", headers: { "Authorization": `Bearer ${API_KEY}` }, }) .then((res) => res.json()) .then((json) => console.log(json)) .catch(console.error); ``` ```python Python theme={"system"} import requests headers = {"Authorization": "Bearer API_KEY"} automation_id = "auto_9xKp2Lm4nQ" r = requests.delete( f"https://api.ayrshare.com/api/automations/{automation_id}", headers=headers, ) print(r.json()) ``` ```php PHP theme={"system"} $id = "auto_9xKp2Lm4nQ"; $curl = curl_init(); curl_setopt_array($curl, [ CURLOPT_URL => "https://api.ayrshare.com/api/automations/" . $id, CURLOPT_RETURNTRANSFER => true, CURLOPT_CUSTOMREQUEST => "DELETE", CURLOPT_HTTPHEADER => ["Authorization: Bearer API_KEY"], ]); $response = curl_exec($curl); curl_close($curl); echo $response; ``` ```json 200: Success theme={"system"} { "status": "success", "id": "auto_9xKp2Lm4nQ" } ``` ```json 404: Not found theme={"system"} { "action": "automation", "status": "error", "code": 469, "message": "Automation not found" } ``` # Get Automation Activity Source: https://www.ayrshare.com/docs/apis/automations/get-activity GET /automations/:id/activity Retrieve the last 30 days of dispatch attempts for one automation, newest first, with recipient, matched trigger, per-action results, and status values. > [!NOTE] > > **Beta.** See the [Automations Overview](/docs/apis/automations/overview) for the full feature description. Returns every dispatch attempt for one automation, newest first. Each row records the recipient, the matched trigger, every action's result, and any error. Rows from the last **30 days** are returned. Older rows exist in Firestore (retention is indefinite for analytics) but are excluded from this endpoint for performance. ## Activity row status Each row has a top-level `status` plus a per-action `status` inside `actionResults[]`. | Status | When it's recorded | | - | - | | `pending` | Row was just written; the worker hasn't picked it up yet | | `in_flight` | The worker is currently dispatching | | `sent` | At least one action was **accepted** by the platform, and none hit an auth error. For `send_dm` this means Meta accepted the message — **not** that the recipient received it (see the note below) | | `failed` | No action was accepted, and at least one was rejected by the platform or could not run (and none hit an auth error). The reason is on `actionResults[].errorDetails` | | `auth_error` | The account's Instagram or Facebook Page credentials or messaging permissions could not be used — for example an expired or revoked access token, a missing Facebook Page selection, or Messaging not activated for the profile. The DM was not retried. Per-recipient delivery failures are recorded as `failed`, not here | | `rate_limited` | The daily DM cap (tier or per-profile) was hit. No DM was sent | | `deduplicated` | This action already fired to this recipient inside its dedup window (default 7 days, per-action configurable) | | `skipped` | The automation was suppressed before dispatch — it became inactive or was deleted between fan-out and dispatch, it has no actions configured, or the engagement that triggered it came from the automation's own account — most often a comment on your own post. In that last case no action runs: no DM (an account cannot message itself), and no `fire_webhook` or `send_email` either. An automation that opted in to [`automation.completed`](/docs/apis/automations/overview#automation-completed) still receives that event, with this status | `pending` and `in_flight` are transient; everything else is terminal. When an automation has more than one action, the top-level `status` is derived from the per-action results in this order: `auth_error` if any action hit an auth error, then `sent` if any action was accepted, then `deduplicated` if every action was deduplicated, then `skipped` if every action was deliberately suppressed (skipped or deduplicated), otherwise `failed`. Read `actionResults[]` for the per-action detail. > [!NOTE] > > **`sent` confirms Meta accepted the message, not that the recipient received it.** This holds for Instagram DMs and for Messenger DMs from a Facebook Page: the automation records acceptance only and does not track delivery. Instagram exposes no delivery signal on any API surface, and if the recipient's **Message requests** setting blocks strangers, Meta accepts the send and silently drops it. See [Automation DM Sent but Not Delivered](/docs/help-center/technical-support/automation-dm-sent-but-not-delivered). ## Per-action result fields Each entry in `actionResults[]` describes one action's outcome: | Field | Type | Description | | - | - | - | | `actionId` | string | The action's stable ID within the automation | | `type` | string | The action type (`send_dm`, `fire_webhook`, `send_email`) | | `status` | string | Per-action status, using the same vocabulary as the row `status` above | | `errorDetails` | string \| `null` | Human-readable failure reason when the action did not succeed; `null` (or absent) on success. For a rejected `send_dm` this carries the mapped error message (e.g. the private-reply window is closed, or the comment already received a reply). One exception to `null` on success: a `send_dm` with `messageSource: "fallback"` is `sent` and carries the reason its callback was not used. A `fallback` that then failed to send carries the send error followed by that reason | | `messageSource` | string | `send_dm` only. Which text the DM was built from: `template` (the static `message`), `callback` (your [`messageUrl`](/docs/apis/automations/overview#dynamic-message-content) answered and its text was used) or `fallback` (`messageUrl` was set but unusable, so `message` was used instead, with the reason on `errorDetails`). It does not say whether the DM was delivered; `status` does. Present once the DM text has been worked out, including sends that then failed. Absent when the action stopped before that (a `skipped` DM to your own account, a `deduplicated` action, or a `failed` configuration error such as a missing comment id), and for other action types | | `config` | object (optional) | The action's configuration as it was when this fire ran, for example the `send_dm` `message` template. Stays the same if you later edit the automation, so each row shows what was configured at the time. Absent on rows written before this field was added, and when it was removed to fit the size limit (`truncated` is then `true`) | | `sent` | object (optional) | What the action sent, or tried to send, for this fire. Recorded once the action built its message or payload, including when the send then failed: `status` says whether it went out. Absent when the action stopped before sending, for example a `skipped` result for a comment from your own account, and for `deduplicated` results. See [what `sent` contains](#what-sent-contains) | | `truncated` | boolean (optional) | `true` when the entry was cut to fit its size limit: a long text value was shortened, or `config` and then `sent` were left out entirely. Absent otherwise | | `completedAt` | string | ISO 8601 timestamp when the action finished | ### What `sent` contains | Action type | `sent` fields | | - | - | | `send_dm` | `message`: the text the DM was sent with. That is your `message` template with its placeholders filled in for this recipient or, when `messageSource` is `callback`, the text your `messageUrl` returned. `fallbackMessage`: present only when `messageSource` is `callback`; your template, filled in, as it would have been sent without the callback | | `send_email` | `to`, `subject` and `message`: the email as it was queued, placeholders filled in | | `fire_webhook` | `payload`: the event fields sent to your webhook. The delivered body also carries the standard webhook fields (`action`, `created`, `hookId`, `refId`, `url`, and `timeStamp` when the webhook is signed). `hookId`: the delivery's id, present when the webhook was queued | Long text values are cut to 4,000 characters, and `truncated` is set when that happens. Values stored under key names that look like credentials, such as `token`, `secret`, `password` or `apiKey`, are recorded as `"[redacted]"`. A `send_dm` action's `messageUrl` is always recorded as `"[redacted]"` too, because a URL can carry a secret in its path or query string. For comment-triggered automations (`comment_keyword`), the row also carries a top-level `commentId` — the raw Instagram comment ID the DM was anchored to. It is `null` for message-driven triggers (`dm_keyword`, `story_reply`, `dm_reaction`). Rows also carry `postId` (the post a `comment_keyword` comment was left on) and `storyId` (the story for `story_reply` and `story_comment`). Each is `null` for the other triggers, and absent on rows written before these fields were added. ## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` **Profile-Key** `string` [Profile Key](/docs/apis/overview#profile-key-format) of a User Profile. Format: `Profile-Key: PROFILE_KEY` ## Path Parameters **id** `string` *required* The automation ID returned from `POST /automations`. ## Query Parameters **limit** `number` (default: 25) Page size. Default `25`, max `100`. Values outside `[1, 100]` are clamped; non-integer or `≤0` returns HTTP 400. **next** `string` Opaque cursor from a previous response's `meta.pagination.next`. Omit on the first request. Forward-only — passing `?previous=` returns HTTP 400. ## Response Shape The response wraps the result list in `activity[]` and the cursor info in `meta.pagination`: ```json theme={"system"} { "status": "success", "automationId": "auto_9xKp2Lm4nQ", "activity": [ /* …rows, newest first… */ ], "meta": { "pagination": { "hasMore": true, "limit": 25, "next": "eyJ0aW1lIjoiMjAyNi0wNS0xMlQwOTowMDoxMS4wMDBaIiwiaWQiOiJhdXRvXzl4S3AyTG00blE6dHJnX2ExYjJjMzpjb21tZW50X3JlY2VpdmVkOjE4MDEyMzQ1Njc4OTAwMDAwIn0=" } } } ``` `meta.pagination.next` is a base64-encoded opaque blob — treat it as a black box and pass it back unchanged in the next request's `?next=`. `meta.pagination.hasMore` is `true` when at least one more page exists. ```bash cURL — first page theme={"system"} curl \ -H "Authorization: Bearer API_KEY" \ -X GET "https://api.ayrshare.com/api/automations/auto_9xKp2Lm4nQ/activity?limit=25" ``` ```bash cURL — next page theme={"system"} curl \ -H "Authorization: Bearer API_KEY" \ -X GET "https://api.ayrshare.com/api/automations/auto_9xKp2Lm4nQ/activity?limit=25&next=eyJ0aW1lIjoiMjAyNi0wNS0xMlQw..." ``` ```javascript JavaScript theme={"system"} const API_KEY = "API_KEY"; const id = "auto_9xKp2Lm4nQ"; // Paginate through every page let next; do { const url = new URL(`https://api.ayrshare.com/api/automations/${id}/activity`); url.searchParams.set("limit", "25"); if (next) url.searchParams.set("next", next); const res = await fetch(url, { headers: { "Authorization": `Bearer ${API_KEY}` }, }); const json = await res.json(); console.log(json.activity); next = json.meta?.pagination?.hasMore ? json.meta.pagination.next : undefined; } while (next); ``` ```python Python theme={"system"} import requests headers = {"Authorization": "Bearer API_KEY"} automation_id = "auto_9xKp2Lm4nQ" params = {"limit": 25} while True: r = requests.get( f"https://api.ayrshare.com/api/automations/{automation_id}/activity", params=params, headers=headers, ).json() print(r["activity"]) pagination = r.get("meta", {}).get("pagination", {}) if not pagination.get("hasMore"): break params["next"] = pagination["next"] ``` ```php PHP theme={"system"} $id = "auto_9xKp2Lm4nQ"; $curl = curl_init(); curl_setopt_array($curl, [ CURLOPT_URL => "https://api.ayrshare.com/api/automations/" . $id . "/activity?limit=25", CURLOPT_RETURNTRANSFER => true, CURLOPT_CUSTOMREQUEST => "GET", CURLOPT_HTTPHEADER => ["Authorization: Bearer API_KEY"], ]); $response = curl_exec($curl); curl_close($curl); echo $response; ``` ```json 200: Success theme={"system"} { "status": "success", "automationId": "auto_9xKp2Lm4nQ", "activity": [ { "id": "auto_9xKp2Lm4nQ:trg_a1b2c3:comment.received:18012345678901234", "automationId": "auto_9xKp2Lm4nQ", "triggerId": "trg_a1b2c3", "triggerType": "comment_keyword", "recipientId": "17841401234567890", "recipientUsername": "jane_doe", "eventId": "comment.received:18012345678901234", "commentId": "18012345678901234", "platform": "instagram", "status": "sent", "actionResults": [ { "actionId": "act_d4e5f6", "type": "send_dm", "status": "sent", "errorDetails": null, "messageSource": "template", "config": { "message": "Hi {{recipient_username}}, here's the link: https://example.com" }, "sent": { "message": "Hi jane_doe, here's the link: https://example.com" }, "completedAt": "2026-05-12T09:14:48.000Z" } ], "keyword": "LINK", "commentText": "Send me the LINK please!", "created": "2026-05-12T09:14:22.000Z", "completedAt": "2026-05-12T09:14:48.000Z" }, { "id": "auto_9xKp2Lm4nQ:trg_a1b2c3:comment.received:18012345678905555", "automationId": "auto_9xKp2Lm4nQ", "triggerId": "trg_a1b2c3", "triggerType": "comment_keyword", "recipientId": "17841405555555555", "recipientUsername": "late_commenter", "eventId": "comment.received:18012345678905555", "commentId": "18012345678905555", "platform": "instagram", "status": "failed", "actionResults": [ { "actionId": "act_d4e5f6", "type": "send_dm", "status": "failed", "errorDetails": "Cannot send private reply: This comment is no longer eligible for a private reply.", "completedAt": "2026-05-12T09:12:03.000Z" } ], "keyword": "LINK", "commentText": "LINK please!", "created": "2026-05-12T09:12:01.000Z", "completedAt": "2026-05-12T09:12:03.000Z" }, { "id": "auto_9xKp2Lm4nQ:trg_a1b2c3:comment.received:18012345678900000", "automationId": "auto_9xKp2Lm4nQ", "triggerId": "trg_a1b2c3", "triggerType": "comment_keyword", "recipientId": "17841409876543210", "recipientUsername": "repeat_user", "eventId": "comment.received:18012345678900000", "commentId": "18012345678900000", "platform": "instagram", "status": "deduplicated", "actionResults": [], "keyword": "LINK", "commentText": "LINK again please", "created": "2026-05-12T09:00:11.000Z", "completedAt": "2026-05-12T09:00:34.000Z" } ], "meta": { "pagination": { "hasMore": true, "limit": 25, "next": "eyJ0aW1lIjoiMjAyNi0wNS0xMlQwOTowMDoxMS4wMDBaIiwiaWQiOiJhdXRvXzl4S3AyTG00blE6dHJnX2ExYjJjMzoxODAxMjM0NTY3ODkwMDAwMCJ9" } } } ``` ```json 400: Bad limit theme={"system"} { "status": "error", "message": "limit must be a positive integer" } ``` ```json 400: Backward pagination not supported theme={"system"} { "status": "error", "message": "'previous' pagination is not supported by this endpoint" } ``` ```json 404: Not found theme={"system"} { "action": "automation", "status": "error", "code": 469, "message": "Automation not found" } ``` # Get Automation Source: https://www.ayrshare.com/docs/apis/automations/get-automation GET /automations/:id Fetch one automation by ID, including every attached trigger and action, its active status, and stats such as totalSent and lastTriggeredAt. > [!NOTE] > > **Beta.** See the [Automations Overview](/docs/apis/automations/overview) for the full feature description. Returns one automation including every attached trigger and action. Soft-deleted automations return `469` so an attacker probing for IDs gets the same response as a legitimate "not found". ## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` **Profile-Key** `string` [Profile Key](/docs/apis/overview#profile-key-format) of a User Profile. Format: `Profile-Key: PROFILE_KEY` ## Path Parameters **id** `string` *required* The automation ID returned from `POST /automations`. ```bash cURL theme={"system"} curl \ -H "Authorization: Bearer API_KEY" \ -X GET https://api.ayrshare.com/api/automations/auto_9xKp2Lm4nQ ``` ```javascript JavaScript theme={"system"} const API_KEY = "API_KEY"; const id = "auto_9xKp2Lm4nQ"; fetch(`https://api.ayrshare.com/api/automations/${id}`, { method: "GET", headers: { "Authorization": `Bearer ${API_KEY}` }, }) .then((res) => res.json()) .then((json) => console.log(json)) .catch(console.error); ``` ```python Python theme={"system"} import requests headers = {"Authorization": "Bearer API_KEY"} automation_id = "auto_9xKp2Lm4nQ" r = requests.get( f"https://api.ayrshare.com/api/automations/{automation_id}", headers=headers, ) print(r.json()) ``` ```php PHP theme={"system"} $id = "auto_9xKp2Lm4nQ"; $curl = curl_init(); curl_setopt_array($curl, [ CURLOPT_URL => "https://api.ayrshare.com/api/automations/" . $id, CURLOPT_RETURNTRANSFER => true, CURLOPT_CUSTOMREQUEST => "GET", CURLOPT_HTTPHEADER => ["Authorization: Bearer API_KEY"], ]); $response = curl_exec($curl); curl_close($curl); echo $response; ``` ```json 200: Success theme={"system"} { "status": "success", "id": "auto_9xKp2Lm4nQ", "platform": "instagram", "accountId": "17841400000000000", "name": "Spring promo", "description": "", "active": true, "stats": { "totalSent": 42, "totalFailed": 1, "lastTriggeredAt": "2026-05-12T09:14:22.000Z", "lastSentAt": "2026-05-12T09:14:48.000Z" }, "created": "2026-04-30T12:00:00.000Z", "updated": "2026-05-12T09:14:48.000Z", "triggers": [ { "id": "trg_a1b2c3", "type": "comment_keyword", "active": true, "config": { "postId": "17895695668004550", "keywords": ["INFO", "LINK"] } } ], "actions": [ { "id": "act_d4e5f6", "type": "send_dm", "active": true, "config": { "message": "Hey {{recipient_username}}, here is the link you wanted: https://example.com" } } ] } ``` ```json 404: Not found theme={"system"} { "action": "automation", "status": "error", "code": 469, "message": "Automation not found" } ``` # List Automations Source: https://www.ayrshare.com/docs/apis/automations/list-automations GET /automations Returns every active and inactive automation for the caller, excluding soft-deleted ones, with an optional platform filter for instagram or facebook. > [!NOTE] > > **Beta.** See the [Automations Overview](/docs/apis/automations/overview) for the full feature description. Returns every active and inactive automation for the caller. Soft-deleted automations are excluded. ## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` **Profile-Key** `string` [Profile Key](/docs/apis/overview#profile-key-format) of a User Profile. Format: `Profile-Key: PROFILE_KEY` ## Query Parameters **platform** `string` Optional. Filter to automations targeting this platform: `instagram` or `facebook`. ```bash cURL theme={"system"} curl \ -H "Authorization: Bearer API_KEY" \ -X GET "https://api.ayrshare.com/api/automations?platform=instagram" ``` ```javascript JavaScript theme={"system"} const API_KEY = "API_KEY"; fetch("https://api.ayrshare.com/api/automations?platform=instagram", { method: "GET", headers: { "Authorization": `Bearer ${API_KEY}` }, }) .then((res) => res.json()) .then((json) => console.log(json)) .catch(console.error); ``` ```python Python theme={"system"} import requests headers = {"Authorization": "Bearer API_KEY"} r = requests.get( "https://api.ayrshare.com/api/automations", params={"platform": "instagram"}, headers=headers, ) print(r.json()) ``` ```php PHP theme={"system"} $curl = curl_init(); curl_setopt_array($curl, [ CURLOPT_URL => "https://api.ayrshare.com/api/automations?platform=instagram", CURLOPT_RETURNTRANSFER => true, CURLOPT_CUSTOMREQUEST => "GET", CURLOPT_HTTPHEADER => ["Authorization: Bearer API_KEY"], ]); $response = curl_exec($curl); curl_close($curl); echo $response; ``` ```json 200: Success theme={"system"} { "status": "success", "automations": [ { "id": "auto_9xKp2Lm4nQ", "platform": "instagram", "accountId": "17841400000000000", "name": "Spring promo", "description": "", "active": true, "stats": { "totalSent": 42, "totalFailed": 1, "lastTriggeredAt": "2026-05-12T09:14:22.000Z", "lastSentAt": "2026-05-12T09:14:48.000Z" }, "created": "2026-04-30T12:00:00.000Z", "updated": "2026-05-12T09:14:48.000Z" } ] } ``` ```json 403: Plan tier theme={"system"} { "action": "automation", "status": "error", "code": 468, "message": "DM automations require a Business or Enterprise plan" } ``` # Automations API Overview Source: https://www.ayrshare.com/docs/apis/automations/overview Build Instagram and Facebook Page rules that send a DM, fire a webhook, or send an email on a comment, a DM or a DM reaction, or an Instagram story reply. > [!NOTE] > > **Beta.** The Automations API is in beta and we are actively collecting feedback. Endpoints, payloads, and limits may change as we iterate. Please send feedback and bug reports to support so we can prioritize the right improvements. The Automations endpoints let you define rules that automatically react to incoming Instagram engagement and to Facebook Page comments and DMs. Each automation pairs one or more **triggers** (the event that fires the rule) with one or more **actions** (what happens when it fires). A single rule can listen for multiple triggers and dispatch multiple actions — fire a webhook into your analytics pipeline AND send a DM from the same engagement. The engine only reacts to engagement a user directs at you (a comment, story reply, DM, or reaction) — it never sends follow-triggered DMs, first-message DMs to strangers, or bulk outbound — and it inherits Ayrshare's per-account rate limits, per-recipient deduplication, and idempotent webhook ingestion. > [!NOTE] > > **`sent` means Meta accepted the message, not that the recipient received it.** On Instagram, delivery is ultimately dictated by the recipient's **Message requests** setting: if they don't allow message requests from everyone, Meta returns a success response and silently drops the message, and this is invisible at every API layer. Even a delivered comment-triggered DM arrives as a **message request the recipient must accept** (unless a conversation already exists between the two accounts). See [Automation DM Sent but Not Delivered](/docs/help-center/technical-support/automation-dm-sent-but-not-delivered) for the full explanation. ## How it works **1. Create an automation** `POST /automations` with the triggers and actions you want. The automation activates immediately. **2. An end user engages** Someone comments on your post, replies to your story, sends a DM, or reacts to a DM. Meta delivers the webhook to Ayrshare. **3. Ayrshare matches and dispatches** A 20–60 second jitter is applied to the engagement to stay inside Instagram's anti-spam heuristics. The engine then looks up every rule matching the event, checks per-action deduplication and your daily DM cap, and runs each action. **4. Inspect what fired** `GET /automations/:id/activity` returns the audit log — every dispatch attempt, the per-action results, and any errors. ## Triggers You can attach up to **50 triggers** to one automation. Each trigger is a discriminated union on the `type` field; type-specific fields live at the same level. The **Platforms** column shows where each trigger is available; see [Facebook Pages](#facebook-pages) for what Facebook needs. | Type | Fires when | Platforms | Config | | - | - | - | - | | `comment_keyword` | A comment matching a keyword lands on a post or reel | `instagram`, `facebook` | `postId` (required — a post ID, or `"all"`); `keywords` (required, ≥1 entry) | | `story_reply` | A user replies to a story via DM | `instagram` | `storyId` (optional — a story ID, or `"all"`) | | `dm_reaction` | A user reacts to one of your DMs with an emoji | `instagram`, `facebook` | `emoji` (optional — omit to fire on any emoji) | | `dm_keyword` | A user sends a DM whose text matches a keyword | `instagram`, `facebook` | `keywords` (required, ≥1 entry) | Keyword matching is **case-insensitive** and whole-word. An event satisfies a keyword-filtered trigger if it contains any one of the configured keywords. ### Evergreen rules with `"all"` Set `postId` (or `storyId`) to the literal lowercase string `"all"` to fire on **every** post, reel, or story on the connected account instead of one specific piece of content. This is what you want for a rule that should keep working as new content is published, with no API call per post: ```json theme={"system"} { "type": "comment_keyword", "postId": "all", "keywords": ["LINK", "INFO"] } ``` The value must be exactly `"all"`. A variant such as `"ALL"` or `" all"` is rejected with a validation error rather than saved as a post ID that would never match. `"all"` never widens beyond the connected account — a rule on one profile cannot fire on another profile's content. Your daily DM cap and the engagement-anchored send rules apply exactly as they do to a single-post rule. > [!WARNING] > > Deduplication is counted per automation and recipient, not per post. On an `"all"` rule, a user who comments a keyword on two different posts within the dedup window (7 days by default) receives the DM only once; the second activity is recorded as `deduplicated`. A smaller [`dedupWindowMinutes`](#per-action-dedup-window) only shortens that window; it does not make deduplication per post. Setting it to `0` turns deduplication off, so every matching comment sends a DM, including repeat comments on the same post. > [!NOTE] > > On the story triggers, omitting `storyId` behaves the same as `"all"`. Prefer `"all"` — it states the intent explicitly, and omission will be retired in a future release with advance notice on [Upcoming API Changes](/docs/whatsnew/upcoming-api-changes). ### Facebook Pages Set `platform` to `facebook` to run an automation on the Facebook Page linked to the profile. DMs are sent from the Page through Messenger. * **Triggers:** `comment_keyword`, `dm_keyword` and `dm_reaction`. Story triggers are Instagram-only, and a Facebook automation that uses one is rejected with a validation error. * **Messaging:** the profile needs [Messaging](/docs/apis/messages/overview) activated for Facebook, the same as sending a Facebook DM with the Messages API. Comments on the Page reach automations only while Messaging is active. * **Recipient:** `{{recipient_username}}` is empty, because Facebook webhooks do not include a username. On DM triggers `{{recipient_id}}` is the user's Page-scoped ID (PSID). ```json theme={"system"} { "platform": "facebook", "name": "Price list on request", "triggers": [{ "type": "dm_keyword", "keywords": ["price"] }], "actions": [{ "type": "send_dm", "message": "Here is our price list: https://example.com/prices" }] } ``` #### Comments on Facebook Page posts A `comment_keyword` automation on `facebook` fires when someone comments on one of the Page's posts with a matching keyword. `send_dm` then answers with a **private reply**: a Messenger message to the commenter, linked to their comment. The commenter does not need to have messaged the Page first. * **`postId`:** the Facebook post ID that `/post` returns in `postIds[].id` (for example `104923907983682_108329000309742`), or `"all"` for every post on the Page. * **Replies count:** a reply to another comment fires the rule too, on the post it belongs to. * **What does not fire:** comments the Page itself writes (including comments and `firstComment` sent through Ayrshare), and edited, hidden or deleted comments. * **One reply per comment:** Meta accepts one private reply per comment, within 7 days of the comment. When Meta refuses one, the activity fails with a code from the [private-reply error range](/docs/errors/errors-ayrshare#automation-private-reply-comment-dm-errors). * **Page admins get no reply:** Meta does not send a private reply to a comment written by an admin of the Page. The rule still fires, and the activity fails with Meta's reason on `errorDetails`: `Cannot Reply to Activity: This user cant reply to this activity`. To test the automation, comment from an account that does not manage the Page. ```json theme={"system"} { "platform": "facebook", "name": "Send the link on request", "triggers": [{ "type": "comment_keyword", "postId": "all", "keywords": ["LINK"] }], "actions": [{ "type": "send_dm", "message": "Thanks for commenting! Here is the link: https://example.com" }] } ``` ## Actions You can attach up to **50 actions** to one automation. They run sequentially; each result is recorded on the activity row. | Type | Effect | Config | | - | - | - | | `send_dm` | Sends a DM (Instagram, or Messenger from your Facebook Page) to the user who triggered the rule, using a [templated message](#template-variables) or [text you generate per fire](#dynamic-message-content). | `message` (required, templated); `messageUrl` (optional); `timeoutMs` (optional) | | `fire_webhook` | POSTs the automation context to your account-level webhook URL (configure via [`POST /hook/webhook`](/docs/apis/webhooks/register)). | `sendCompletedEvent` (optional, boolean — also send [`automation.completed`](#automation-completed)). Payload shape is fixed; see [below](#fire_webhook-payload) | | `send_email` | Queues an email via the platform's mail pipeline. | `to` (required, email); `subject` (optional, templated); `message` (required, templated) | ### Per-action dedup window Every action — regardless of type — additionally accepts an optional top-level `dedupWindowMinutes` field that overrides the **default 7-day** per-recipient dedup window for that action only. * Set to `0` to **disable** dedup entirely for that action (typical for `fire_webhook` / `send_email` where the receiver expects every event). * Capped at `525600` (one year). ```json Action with a 24h dedup override theme={"system"} { "type": "send_dm", "message": "Thanks {{recipient_username}}!", "dedupWindowMinutes": 1440 } ``` ### Dynamic message content By default, `send_dm` sends the same `message` to everyone who triggers the rule. Placeholders such as `{{recipient_username}}` are the only part that changes from person to person. Set **`messageUrl`** on the action and your own server writes the DM for each person instead: every time the rule matches, we ask your endpoint what to say, then send its answer as the DM. **Example.** Your rule replies to anyone who comments "LINK" on a post, and `messageUrl` points at your server. When Ava comments "LINK please!": 1. We `POST` the details to your `messageUrl`: who commented (`"recipientUsername": "ava"`), what they wrote, and which post it was on. 2. Your server writes a reply for Ava, for example with a tracking link made just for her, and answers `{ "message": "Hi ava, here's your link: https://example.com/r/12345" }`. 3. We send that text to Ava as the DM, exactly as you wrote it. If your server does not answer within `timeoutMs`, returns an error, or returns something we cannot use, we send Ava the action's regular `message` instead, so she still gets a reply. Add it to a `send_dm` action like this: ```json send_dm with messageUrl theme={"system"} { "type": "send_dm", "messageUrl": "https://yourapp.example.com/automations/message", "timeoutMs": 3000, "message": "Thanks for commenting! Here is the link: https://example.com" } ``` `messageUrl` is where we ask for the text. `message` is still required and is the fallback: it is never sent to your server, and we DM it instead whenever your server does not answer in time, returns an error, or returns something we cannot use (see the warning below). `timeoutMs` is optional (default 3000, maximum 5000). **What we send your endpoint.** A `POST` with this JSON body: ```json Callback request body theme={"system"} { "automationId": "auto_9xKp2Lm4nQ", "triggerId": "trg_a1b2c3", "trigger": "comment_keyword", "platform": "instagram", "recipientId": "17841401234567890", "recipientUsername": "ava", "keyword": "LINK", "activityId": "auto_9xKp2Lm4nQ:trg_a1b2c3:comment.received:17912345678901234", "actionId": "act_dm_1", "eventId": "comment.received:17912345678901234", "commentId": "17912345678901234", "commentText": "LINK please!", "postId": "17998765432109876", "storyId": null, "timestamp": "2026-05-12T09:14:22.000Z", "timeStamp": 1778577262 } ``` | Field | Meaning | | - | - | | `automationId`, `triggerId`, `trigger`, `platform` | Which automation and trigger fired, as on the [`fire_webhook` payload](#fire_webhook-payload) | | `recipientId`, `recipientUsername` | The person the DM goes to. `recipientUsername` is `null` when the trigger does not carry one (for example `dm_keyword`) | | `keyword` | The keyword that matched, or `null` | | `activityId` | The activity entry for this fire, so you can match it to [`GET /automations/:id/activity`](/docs/apis/automations/get-activity) | | `actionId` | Which `send_dm` action is asking, matching `actionResults[].actionId`. Lets one endpoint serve several `send_dm` actions on the same automation | | `eventId` | Our id for the engagement that caused the fire | | `commentId` | The Instagram comment id for comment triggers; `null` for DM and story triggers | | `commentText` | The text that matched: the comment, the DM, or the reaction emoji. `null` when there is none (for example `story_reply`) | | `postId`, `storyId` | The post or story the engagement landed on, or `null` | | `timestamp` | When we made the request, ISO 8601 | | `timeStamp` | The same moment as a Unix time **in seconds**. Present only when the callback is signed, as on our webhooks | If you have registered a [webhook secret](/docs/apis/webhooks/register), the body is signed with the same secret as your `automations` webhooks, in the same way: `X-Authorization-Content-SHA256` is the HMAC-SHA256 of the raw body, and `X-Authorization-Timestamp` carries the body's `timeStamp`. The verification code you already use for our webhooks works unchanged. If a secret is registered but we cannot read it at that moment, we do **not** send the callback unsigned; we send your `message` template instead. **Where it can point.** `messageUrl` must be a public HTTPS address. A URL that resolves to a private, loopback, link-local or cloud-metadata address is refused, and redirects are not followed — a `3xx` answer counts as a failure. Either way the template is sent instead. **What your endpoint sends back.** Return `200` with a JSON body, at most 16 KB, containing a `message` string of 1 to 1000 characters: ```json theme={"system"} { "message": "Hi ava — grabbed that link for you: https://example.com/r/12345" } ``` Your text is sent **verbatim**. `{{placeholder}}` substitution is not applied to it, so a literal `{{` in your generated text reaches the recipient as written. > [!WARNING] > > **`message` stays required, and it is not decoration.** If your endpoint times out, returns a non-2xx, returns a body we cannot use, or has been temporarily circuit-broken after repeated failures, we send the rendered `message` template instead. That send can still fail, for example if the template renders empty (see below) or Instagram rejects it. We never retry the callback — a retry risks sending the recipient two messages. `timeoutMs` defaults to **3000**. The maximum is **5000**: a larger value is rejected with a validation error when you create or update the automation. It covers the whole exchange, including reading your response body. Budget for it: the DM is sent after your endpoint answers, so a slow endpoint delays the message. Check which path a fire took on [`GET /automations/:id/activity`](/docs/apis/automations/get-activity). A `send_dm` entry in `actionResults[]` carries `messageSource` once the DM text has been worked out, including when the send then failed. It is absent when the action stopped before that, for example a `skipped` DM to your own account or a `deduplicated` action: | `messageSource` | Meaning | | - | - | | `template` | No `messageUrl` configured; the static `message` was used | | `callback` | Your endpoint answered and its text was used | | `fallback` | `messageUrl` was set but unusable; `message` was used instead | `messageSource` says which text the DM was built from, not whether it was delivered. Read the entry's `status` for that. A fallback on its own does not fail the action: when the template DM is delivered, the entry is `sent`, with the reason the callback was not used on `errorDetails`. If the template DM then fails too, the entry is `failed` and `errorDetails` gives the send error followed by the callback reason. Watch for it: a callback failing on every fire otherwise looks exactly like one that is working. Keep your `message` sendable on its own. If it renders empty for a recipient, for example `{{recipient_username}}` alone on a DM trigger that carries no username, and your callback text is not used, no DM is sent and the action is `failed` with the reason on `errorDetails`. ### Webhook events Two events are delivered to the `automations` webhook action you register with [`POST /hook/webhook`](/docs/apis/webhooks/register). Both carry a **`type`** field — switch on it before reading anything else. | `type` | Sent when | | - | - | | `automation.triggered` | The rule's `fire_webhook` action runs | | `automation.completed` | The dispatch finished, with its outcome | > [!NOTE] > > `automation.completed` is off by default. Turn it on for an automation by setting `"sendCompletedEvent": true` on its `fire_webhook` action. It stays off otherwise, so a receiver built for one payload per fire keeps getting exactly one. `automation.triggered` is not a record of every match: it is sent only when `fire_webhook` itself runs, so a match that ends `rate_limited` or `deduplicated` produces `automation.completed` alone. Neither event is sent for an automation that was deactivated or deleted before the dispatch ran. #### `automation.triggered` ```json theme={"system"} { "type": "automation.triggered", "automationId": "auto_9xKp2Lm4nQ", "triggerId": "trg_a1b2c3", "trigger": "comment_keyword", "platform": "instagram", "recipientId": "17841401234567890", "recipientUsername": "jane_doe", "keyword": "LINK", "activityId": "auto_9xKp2Lm4nQ:trg_a1b2c3:comment.received:17912345678901234", "actionId": "act_wh_1", "eventId": "comment.received:17912345678901234", "commentId": "17912345678901234", "commentText": "LINK please", "postId": "17895695668004550", "storyId": null, "timestamp": "2026-05-12T09:14:22.000Z" } ``` `activityId` is the row `GET /automations/:id/activity` returns, so you can join the two exactly rather than matching on `recipientId` + `triggerId` + `keyword` — which is ambiguous when several people comment the same keyword at once. `actionId` is the `fire_webhook` action that sent the event, and matches `actionResults[].actionId`; it tells two `fire_webhook` actions on the same rule apart. Fields a trigger doesn't populate are `null` rather than omitted, so the shape is stable. `recipientUsername` is `null` for `dm_keyword`, `dm_reaction` and `story_reply`, and for every Facebook trigger, because Meta's payload doesn't include one. `commentId` is set only for `comment_keyword` and `story_comment`, `postId` only for `comment_keyword`, and `storyId` only for the story triggers. `story_reply` has no `keyword`. For `dm_keyword`, `commentText` is the DM text; for `dm_reaction`, `commentText` and `keyword` are both the reaction emoji. #### `automation.completed` Sent only when the automation opts in on its `fire_webhook` action: ```json fire_webhook action with the outcome event on theme={"system"} { "type": "fire_webhook", "sendCompletedEvent": true } ``` ```json theme={"system"} { "type": "automation.completed", "automationId": "auto_9xKp2Lm4nQ", "triggerId": "trg_a1b2c3", "trigger": "comment_keyword", "platform": "instagram", "recipientId": "17841401234567890", "activityId": "auto_9xKp2Lm4nQ:trg_a1b2c3:comment.received:17912345678901234", "eventId": "comment.received:17912345678901234", "status": "sent", "actionResults": [ { "actionId": "act_dm_1", "type": "send_dm", "status": "sent", "errorDetails": null, "messageSource": "callback" }, { "actionId": "act_wh_1", "type": "fire_webhook", "status": "sent", "errorDetails": null } ], "errorDetails": null, "timestamp": "2026-05-12T09:14:58.000Z" } ``` `status` and each `actionResults[].status` use the same values as the [activity log](#activity-statuses) — `sent`, `failed`, `rate_limited`, `deduplicated`, `skipped`, `auth_error` — so in the normal case you learn how a dispatch ended without polling. Delivery of this event is best-effort, though: if the process stops between recording the outcome and queuing the event, it is not sent. The activity log is the record of truth, so if you need every outcome, reconcile against `GET /automations/:id/activity` for any `activityId` that has not reported one. A `send_dm` result also carries `messageSource`, as on the [activity log](#dynamic-message-content): `template`, `callback` or `fallback`, so you can spot a `messageUrl` that keeps falling back without polling. Match results to your configured actions by `actionId`, not by position — two actions of the same type are only distinguishable that way. `actionResults` is empty in three cases, each with the reason on the top-level `errorDetails` where there is one: `rate_limited` (your daily cap was reached), `deduplicated` (every action was inside its dedup window), and `failed` after the dispatch was retried the maximum number of times without finishing. In that last case some actions may already have run, so a DM may have gone out and `automation.triggered` may already have been sent. Webhook delivery is at-least-once, so the same event can occasionally arrive twice, and a dispatch that is retried can send `automation.triggered` again with a new `hookId`. Never deduplicate on `hookId`. Key each event type separately, so that processing one never suppresses the other: `automation.completed` on `activityId` (each activity has one outcome), and `automation.triggered` on `activityId` plus `actionId` (one per `fire_webhook` action). The two events are delivered independently, so `automation.completed` can occasionally arrive before `automation.triggered`. #### If no webhook is registered `fire_webhook` posts to the `automations` action registered on the profile, falling back to your Primary Profile. There is **no** fallback to another action such as `messages`. If neither registration exists, `automation.triggered` is not silently dropped: the action is recorded on the activity row as `failed`, with the reason in `actionResults[].errorDetails`. (`automation.completed` has nowhere to go either, and is not recorded.) The dispatch's overall status then follows the usual precedence: `auth_error` if any action hit an auth error, otherwise `sent` if any action was accepted, otherwise `failed`. So the missing webhook counts toward the automation's failure stats only when nothing else on the rule sent; when another action did send (and none hit an auth error), the dispatch ends `sent` and the failed webhook shows only in `actionResults[]`. A `fire_webhook` result of `sent` means the event was queued for delivery to your URL, not that your endpoint accepted it. Delivery is then retried on its own schedule. ## Template variables `send_dm.message`, `send_email.subject`, and `send_email.message` support `{{placeholder}}` substitution. **Unknown placeholders are rejected at create/update time** (as a `473` validation error) so a typo never silently leaks the literal `{{foo}}` into a customer-facing message. | Placeholder | Resolves to | | - | - | | `{{recipient_username}}` | The engaging user's Instagram handle, on comment triggers. Empty on DM and story-reply triggers, and always on Facebook | | `{{recipient_id}}` | The engaging user's ID: the Instagram-scoped ID (IGSID) on Instagram, the Page-scoped ID (PSID) on Facebook | | `{{recipient_name}}` | Reserved; resolves to empty until a future enrichment source populates it | | `{{sender_username}}` | Reserved; resolves to empty on every platform for now | | `{{sender_name}}` | Reserved; resolves to empty on every platform for now | | `{{comment_text}}` | The text of the comment / DM / story reply that fired the trigger | | `{{comment_id}}` | The platform id of the comment / message that fired | | `{{comment_sent_at}}` | ISO 8601 timestamp of the event (when available) | | `{{matched_keyword}}` | The keyword that matched (or the emoji string for `dm_reaction`) | | `{{platform}}` | Platform identifier: `instagram` or `facebook` | | `{{trigger_type}}` | Trigger type (e.g. `comment_keyword`) | > [!NOTE] > > **No `sender_email` / `recipient_email`.** These are deliberately not exposed — your billing email has no legitimate place in a DM to a stranger, and Meta does not provide the recipient's email on any IG webhook. Avoiding the placeholders prevents accidental disclosure. Example template: ``` Hey {{recipient_username}}, thanks for the comment "{{comment_text}}" — here is the link you wanted: https://example.com ``` ## Rate limits and caps | Plan | Active automations (per profile) | Daily DM cap (per account) | | - | - | - | | Launch & Business | 10 | 1,000 | | Enterprise | 50 | 5,000 | The active-automation cap is counted **per [User Profile](/docs/apis/profiles/overview)**, not per parent account. Each profile under your account gets its own Launch/Business 10 or Enterprise 50, so an account with many profiles can run that many automations on each. It counts active automations and is enforced on both `POST` (create) and `PUT` re-activation (`active: false → true`), each surfacing error code `470`. Need a higher per-profile limit? [Contact support](mailto:support@ayrshare.com) to have it raised for your account. The **daily DM cap** applies per parent Ayrshare account, shared across all your profiles, with a per-profile sub-cap so one busy profile cannot drain the whole account's quota. When a DM cap is hit, the activity row records status `rate_limited` and no DM is sent. The **messaging conversation limit** also applies. `send_dm` needs the Messaging add-on on the profile, and each DM it sends counts toward that profile's monthly [conversation limit](/docs/apis/messages/overview#conversation-limit-&-pricing), the same as a DM you send yourself. When the limit is reached, a `send_dm` to a new recipient is not sent and the activity row records status `failed`, with the limit message in `actionResults[].errorDetails`. DMs triggered by a comment (`comment_keyword` and `story_comment`) are the exception: they are still sent at the limit, and still counted. Structural caps on a single automation: **1–50 triggers**, **1–50 actions**. Instagram itself caps DMs at roughly 200/hour per account. The engine paces dispatch with a 20–60 second jitter to stay safely under this. ## Activity statuses A row in `GET /automations/:id/activity` carries a top-level `status` plus a per-action `status` inside `actionResults[]`: | Status | Meaning | | - | - | | `pending` | Just written; the worker hasn't picked it up yet | | `in_flight` | Worker is currently dispatching | | `sent` | At least one action was **accepted** by the platform, and none hit an auth error. For `send_dm` this means Meta accepted the message — **not** that the recipient received it (see the note below) | | `failed` | No action was accepted, and at least one was rejected by the platform or could not run (and none hit an auth error). The reason is on `actionResults[].errorDetails` | | `auth_error` | The account's Instagram or Facebook Page credentials or messaging permissions could not be used — for example an expired or revoked access token, a missing Facebook Page selection, or Messaging not activated for the profile. The DM was not retried. Per-recipient delivery failures are recorded as `failed`, not here | | `rate_limited` | The daily DM cap (tier or per-profile) was hit; no DM was sent | | `deduplicated` | This action already fired to this recipient inside its dedup window | | `skipped` | The automation was suppressed before dispatch — it became inactive or was deleted between fan-out and dispatch, it has no actions configured, or the engagement that triggered it came from the automation's own account — most often a comment on your own post. In that last case no action runs: no DM (an account cannot message itself), and no `fire_webhook` or `send_email` either. An automation that opted in to [`automation.completed`](/docs/apis/automations/overview#automation-completed) still receives that event, with this status | `pending` and `in_flight` are transient; everything else is terminal. When an automation has more than one action, the top-level `status` is derived from the per-action results in this order: `auth_error` if any action hit an auth error, then `sent` if any action was accepted, then `deduplicated` if every action was deduplicated, then `skipped` if every action was deliberately suppressed (skipped or deduplicated), otherwise `failed`. Read `actionResults[]` for the per-action detail. > [!NOTE] > > **`sent` is a platform-acceptance receipt, not a delivery receipt.** A `send_dm` action is marked `sent` the moment Meta accepts the message, whether it is sent from your Instagram account or through Messenger from your Facebook Page. The automation records that acceptance only; it does not track delivery. Instagram does not expose message delivery to any API, and whether the recipient actually receives it depends on their Instagram **Message requests** setting, which Ayrshare cannot read or influence. See [Automation DM Sent but Not Delivered](/docs/help-center/technical-support/automation-dm-sent-but-not-delivered). ## Error codes The API returns two shapes of error: * **Business-rule errors** carry a numbered automation `code` (e.g. `{ "action": "automation", "code": 469, ... }`). * **Validation errors** — any malformed request body (missing or invalid fields, unknown template variables, unrecognized keys) — are returned as a single **`473`** response with a `details` object that lists the offending fields. `details` is the validator's output (`formErrors` plus `fieldErrors`). Branch on `details`, not on a per-condition code. In `fieldErrors`, keys are the top-level request fields (`triggers`, `actions`): a problem inside a specific entry, such as a trigger missing its `keywords`, is reported under that field (e.g. `triggers`), while `formErrors` holds object-level issues such as unrecognized keys. | Code | HTTP | Meaning | | - | - | - | | 468 | 403 | Business or Enterprise plan required | | 469 | 404 | Automation not found (also returned when the caller doesn't own it) | | 470 | 429 | Active automation cap reached for your plan tier | | 471 | 400 | No social account linked on the requested platform for this profile | | 472 | 403 | Feature not yet available on your account — contact us for early access | | 473 | 400 | Validation failed (malformed request body) — inspect `details` | > [!NOTE] > > **Launch plans are included.** Business Launch resolves to the Business tier, so Automations > are available on Launch, Business, and Enterprise. `468` is only returned to plans that do > not include Automations. ## What Meta does NOT allow A few commonly-requested capabilities are not supported because Meta doesn't permit them on the public Instagram API: * **Auto-DM on new followers.** Instagram does not publish a follow webhook. * **First-message DMs to strangers.** Meta requires the recipient to have engaged first (comment, reply, DM, reaction) before a business account can message them. Every supported trigger is anchored to such an engagement — but note that being *allowed* to send is not the same as the message being *delivered*: the recipient's Instagram **Message requests** setting can still cause Meta to accept and then silently drop the message (see [Automation DM Sent but Not Delivered](/docs/help-center/technical-support/automation-dm-sent-but-not-delivered)). * **Bulk outbound campaigns.** Hourly DM caps and anti-abuse heuristics apply at the platform level. ## Multi-profile usage The endpoints respect the `profileKey` header. Pass a child profile's key and the automation is created/managed under that profile. Rate limits split across profiles via a per-profile sub-cap so one chatty profile doesn't drain the parent account's quota. ## FAQ > [accordion] Can I trigger on a new follower? > > No. Instagram does not publish a follow webhook, and Meta does not allow third-party apps to send a DM to a user who has not engaged first. Every supported trigger (`comment_keyword`, `story_reply`, `dm_reaction`, `dm_keyword`) is anchored to such an engagement, which is what makes the send permissible. > [accordion] Why does an activity say `sent` but the recipient never got the DM? > > `sent` means Meta **accepted** the message, not that it was delivered. This is true on both Instagram and Facebook (Messenger from your Page): the automation records acceptance only and does not track delivery. On Instagram, delivery depends on the recipient's Instagram **Message requests** setting — if they don't allow message requests from everyone, Meta returns a success response and silently drops the message, with no error, webhook, or other signal on any API surface. A successful comment-triggered DM also arrives as a **message request the recipient must accept** (unless a conversation already exists). This is a permanent Instagram platform limitation. See [Automation DM Sent but Not Delivered](/docs/help-center/technical-support/automation-dm-sent-but-not-delivered). > [accordion] What happens if my access token is invalid when an automation fires? > > If the account's Instagram or Facebook Page credentials or messaging permissions genuinely fail — for example an expired or revoked access token, a missing Facebook Page selection, or Messaging not activated for the profile — the activity row records status `auth_error` and the DM is not retried. Resolving the account state (relinking, selecting a Page, or activating Messaging) lets the next matching engagement fire normally. This only applies to genuine credential and permission failures: if a specific DM could not be delivered for another reason — for example the recipient could not be found, or the triggering engagement came from the account that would send the DM — the row is recorded as `failed` or `skipped` with the reason on `actionResults[].errorDetails`, and relinking will not change the outcome. > [accordion] Why is there a delay before the DM is sent? > > Each dispatch is scheduled 20–60 seconds after the engagement, so DM sends look organic to Instagram's anti-spam systems. The delay is applied to the engagement **before** any rule is matched, so it applies to every action on the rule, `fire_webhook` and `send_email` included — expect your webhook to arrive 20–60 seconds after the comment, not immediately. The activity row's `created` timestamp is when the trigger matched; `completedAt` is when dispatch finished. > [accordion] Are activity rows kept forever? > > Activity rows are retained indefinitely for trace and analytics. The `GET /automations/:id/activity` endpoint returns rows from the last 30 days for performance. (The dedup guard uses its own per-action window — defaulting to 7 days — which is unrelated to the activity lookback.) > [accordion] Does deleting an automation remove its activity history? > > No. Delete is a soft-delete: the master row is flagged `deleted`, no new dispatches occur, but historical activity rows remain readable via the activity endpoint. ## Endpoints * [`POST /automations`](/docs/apis/automations/create-automation) — create a new automation * [`GET /automations`](/docs/apis/automations/list-automations) — list your automations * [`GET /automations/:id`](/docs/apis/automations/get-automation) — fetch one automation with its triggers and actions * [`PUT /automations/:id`](/docs/apis/automations/update-automation) — partial update; pause via `active: false` * [`DELETE /automations/:id`](/docs/apis/automations/delete-automation) — soft-delete * [`GET /automations/:id/activity`](/docs/apis/automations/get-activity) — cursor-paginated dispatch audit log # Update Automation Source: https://www.ayrshare.com/docs/apis/automations/update-automation PUT /automations/:id Partially update an automation's name, description, active status, triggers, or actions. Sending triggers or actions replaces the existing set atomically. > [!NOTE] > > **Beta.** See the [Automations Overview](/docs/apis/automations/overview) for the full feature description. Partial update. Only the listed fields are editable; sending anything else (e.g. `platform`, `accountId`) is rejected as a validation error. Sending `triggers` or `actions` **replaces** the existing children atomically: the old set is deleted and the new set is written in a single Firestore transaction. To change only one trigger, send the full new array. Re-activating a previously-inactive automation (`active: true`) re-checks the active-cap. If the cap is already at the limit, the update fails with code `470` and no changes are made. ## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` **Profile-Key** `string` [Profile Key](/docs/apis/overview#profile-key-format) of a User Profile. Format: `Profile-Key: PROFILE_KEY` ## Path Parameters **id** `string` *required* The automation ID returned from `POST /automations`. ## Body Parameters **name** `string` New name. **description** `string` New description. **active** `boolean` Toggle the automation on or off. Setting this from `false` to `true` re-checks the active automation cap. **triggers** `array` Full replacement set of triggers (1–50). Same shape as the create endpoint. **actions** `array` Full replacement set of actions (1–50). Same shape as the create endpoint. ```bash cURL theme={"system"} curl \ -H "Authorization: Bearer API_KEY" \ -H 'Content-Type: application/json' \ -d '{ "name": "Spring promo (updated)", "active": true, "actions": [ { "type": "send_dm", "message": "Hey {{recipient_username}}! New link: https://example.com/spring" } ] }' \ -X PUT https://api.ayrshare.com/api/automations/auto_9xKp2Lm4nQ ``` ```javascript JavaScript theme={"system"} const API_KEY = "API_KEY"; const id = "auto_9xKp2Lm4nQ"; fetch(`https://api.ayrshare.com/api/automations/${id}`, { method: "PUT", headers: { "Authorization": `Bearer ${API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ name: "Spring promo (updated)", active: true, actions: [ { type: "send_dm", message: "Hey {{recipient_username}}! New link: https://example.com/spring", }, ], }), }) .then((res) => res.json()) .then((json) => console.log(json)) .catch(console.error); ``` ```python Python theme={"system"} import requests headers = { "Authorization": "Bearer API_KEY", "Content-Type": "application/json", } automation_id = "auto_9xKp2Lm4nQ" payload = { "name": "Spring promo (updated)", "active": True, "actions": [ { "type": "send_dm", "message": "Hey {{recipient_username}}! New link: https://example.com/spring", } ], } r = requests.put( f"https://api.ayrshare.com/api/automations/{automation_id}", json=payload, headers=headers, ) print(r.json()) ``` ```php PHP theme={"system"} $id = "auto_9xKp2Lm4nQ"; $data = [ "name" => "Spring promo (updated)", "active" => true, "actions" => [ [ "type" => "send_dm", "message" => "Hey {{recipient_username}}! New link: https://example.com/spring", ], ], ]; $curl = curl_init(); curl_setopt_array($curl, [ CURLOPT_URL => "https://api.ayrshare.com/api/automations/" . $id, CURLOPT_RETURNTRANSFER => true, CURLOPT_CUSTOMREQUEST => "PUT", CURLOPT_POSTFIELDS => json_encode($data), CURLOPT_HTTPHEADER => [ "Authorization: Bearer API_KEY", "Content-Type: application/json", ], ]); $response = curl_exec($curl); curl_close($curl); echo $response; ``` ```json 200: Success theme={"system"} { "status": "success", "id": "auto_9xKp2Lm4nQ" } ``` ```json 400: Validation failed — editing platform is not allowed theme={"system"} { "action": "request", "status": "error", "code": 473, "message": "Validation failed. Please see: https://www.ayrshare.com/docs/introduction", "details": { "formErrors": ["Unrecognized key: \"platform\""], "fieldErrors": {} } } ``` ```json 404: Not found theme={"system"} { "action": "automation", "status": "error", "code": 469, "message": "Automation not found" } ``` ```json 429: Cap reached on reactivation theme={"system"} { "action": "automation", "status": "error", "code": 470, "message": "Active automation limit reached for your plan tier" } ``` # Brand API: Public Account & Competitor Data Source: https://www.ayrshare.com/docs/apis/brand/overview Use the Ayrshare Brand API to pull public profile, follower, and engagement data for any social account, to track your brand or benchmark competitors. > [!NOTE] > > **These endpoints have moved.** The Brand section has been reorganized under the new [Listening](/docs/apis/listen/overview) section. All existing Brand endpoints continue to work, but new features like [Keyword Search](/docs/apis/listen/keyword/search-tweets) are available only in the Listening section. # Delete Comments Source: https://www.ayrshare.com/docs/apis/comments/delete-comments DELETE /comments/:id Delete a single comment or all comments under a post sent via Ayrshare, or delete comments sent outside of Ayrshare using the Social Comment ID. > [!NOTE] > > **Targeting X/Twitter?** Starting March 31, 2026, all X operations require your own API credentials. After linking X via OAuth, include these 2 headers in your request: > > `X-Twitter-OAuth1-Api-Key` — Your API Key (Consumer Key) > > `X-Twitter-OAuth1-Api-Secret` — Your API Key Secret (Consumer Secret) > > **One-time setup per Ayrshare account.** You create one X Developer App and reuse the same API Key and Secret across every sub-profile / end-user you link. You do *not* create a new app per customer. > > Not linked yet? See the [full setup guide](/docs/dashboard/connect-social-accounts/x-twitter-byo-keys) to connect your X account. > > Your keys are never logged or stored by Ayrshare. The delete endpoint allows you to either delete comments sent via Ayrshare or comments that were sent outside of Ayrshare. Please see the [Comments Overview](/docs/apis/comments/overview) for more information on the different ID types. ### Delete Comments Sent from Ayrshare Delete either a single comment or all comments under a post that were sent via Ayrshare. Please see the [Ayrshare Post ID](/docs/apis/comments/overview#comments-with-ayrshare-post-id) and [Ayrshare Comment ID](/docs/apis/comments/overview#comments-with-ayrshare-comment-id) for more information. Supported platforms: Bluesky, Facebook, Instagram, LinkedIn, Reddit, TikTok, X/Twitter, and YouTube. > [!NOTE] > > **YouTube comments older than 28 days.** Ayrshare keeps the record of a YouTube comment it sent for 28 days, to stay within the 30-day limit in YouTube's API policies. After that, the Ayrshare ID no longer finds the comment. To delete it, use its [Social Comment ID](/docs/apis/comments/overview#comments-with-social-comment-id) (the YouTube comment `commentId`) with `"searchPlatformId": true`. ### Delete Comments Sent Outside of Ayrshare Delete a comment that was not sent via Ayrshare by using the `commentId` returned for a particular social network. This is the [Social Comment ID](/docs/apis/comments/overview#comments-with-social-comment-id) from the social networks, not the Ayrshare ID. Supported platforms: Bluesky, Facebook, Instagram, Threads, TikTok, X/Twitter, and YouTube. > [!NOTE] > > **TikTok — own-authored comments only.** On TikTok, `DELETE /comments` only succeeds for comments authored by the authenticated TikTok account itself (for example, replies you posted via Ayrshare's [reply endpoint](/docs/apis/comments/reply-to-comment) or directly inside the TikTok app). Attempting to delete a comment authored by another user — even one left on a video you own — returns Ayrshare `code: 328`. If you need to moderate third-party comments on your own TikTok videos, contact support so we can confirm what's available for your linked account type. For example, you get all the comments for a particular Instagram post using the [Get Comments](/docs/apis/comments/get-comments) endpoint with the `searchPlatformId` set to `true`. ```http theme={"system"} GET https://api.ayrshare.com/api/comments/18231730279304111?platform=instagram&searchPlatformId=true ``` The returned JSON will have a `commentId` for each comment, which you can use to delete the comment. Remember to set the `searchPlatformId` to `true`. ```json {5} theme={"system"} { "instagram": [ { "comment": "What an amazing comment", "commentId": "17969247335804735", "created": "2024-11-26T11:49:00Z", "from": { "id": "103038435208332", "username": "john_smith" }, "hidden": false, "likeCount": 3, "platform": "instagram", "postId": "18231730279304333", "username": "john_smith" } ] } ``` ## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` **Profile-Key** `string` [Profile Key](/docs/apis/overview#profile-key-format) of a User Profile. Format: `Profile-Key: PROFILE_KEY` ## Path Parameters **id** `string` *required* Delete a comments sent from Ayrshare:
    • Delete all comments sent via Ayrshare by providing the [Ayrshare Post ID](/docs/apis/comments/overview#comments-with-ayrshare-post-id).
    • Delete a single comment by providing the [Ayrshare Comment ID](/docs/apis/comments/overview#comments-with-ayrshare-comment-id).
    Delete a comment sent outside of Ayrshare:
    • Delete a single comment by providing the [Social Comment ID](/docs/apis/comments/overview#comments-with-social-comment-id) from the social network.
    • Must include the `searchPlatformId` set to `true`.
    ## Body Parameters **platforms** `array` Required if deleting comments sent via Ayrshare. The platforms to delete comments from. Supported platforms: `bluesky`, `facebook`, `instagram`, `linkedin`, `reddit`, `threads`, `tiktok`, `twitter`, `youtube`. ```json Deleting comments sent via Ayrshare theme={"system"} DELETE /comments/:id // Ayrshare Post ID or Ayrshare Comment ID { "platforms": ["bluesky", "facebook", "instagram", "linkedin", "reddit", "threads", "tiktok", "twitter", "youtube"] } ``` **platform** `string` Required if deleting using the [Social Comment ID](/docs/apis/comments/overview#comments-with-social-comment-id), which is the `commentId` from the social networks. Supported platforms: `bluesky`, `facebook`, `instagram`, `threads`, `tiktok`, `twitter`, `youtube`. Only one platform is supported at a time. **searchPlatformId** `boolean` (default: false) Required if deleting using the [Social Comment ID](/docs/apis/comments/overview#comments-with-social-comment-id), which is the `commentId` from the social networks, set to `true`. ```json Deleting comments with Social Comment ID theme={"system"} DELETE /comments/:id // Social Comment ID { "searchPlatformId": true, // bluesky, facebook, instagram, threads, tiktok, twitter, youtube "platform": "facebook" } ``` **hide** `boolean` (default: false) TikTok only. Set to `true`, together with `videoId`, to hide a TikTok comment from public viewers instead of deleting it. The success response returns `action: "hide"` and echoes the comment. Sending `hide=true` without `videoId` returns HTTP `400`. Hidden comments remain visible to the video owner in TikTok Studio. ```json Hiding a TikTok comment theme={"system"} DELETE /comments/:id // Social Comment ID { "searchPlatformId": true, "platform": "tiktok", "hide": true, "videoId": "7303719953248109358" } ``` **videoId** `string` Required when hiding a TikTok comment (`hide=true`). The TikTok video ID the comment belongs to. ```bash cURL theme={"system"} curl \ -H "Authorization: Bearer API_KEY" \ -X DELETE https://api.ayrshare.com/api/comments/Ut1fWU6XkqkMayHGnJZ ``` ```javascript JavaScript theme={"system"} const API_KEY = "API_KEY"; fetch("https://api.ayrshare.com/api/comments/Ut1fWU6XkqkMayHGnJZ", { method: "DELETE", headers: { Authorization: `Bearer ${API_KEY}`, }, }) .then((res) => res.json()) .then((json) => console.log(json)) .catch(console.error); ``` ```python Python theme={"system"} import requests headers = {'Authorization': 'Bearer API_KEY'} r = requests.delete('https://api.ayrshare.com/api/comments/Ut1fWU6XkqkMayHGnJZ', headers=headers) print(r.json()) ``` ```php PHP theme={"system"} true, CURLOPT_CUSTOMREQUEST => 'DELETE', CURLOPT_HTTPHEADER => $headers ]); $response = curl_exec($curl); if ($response === false) { echo 'Curl error: ' . curl_error($curl); } else { echo json_encode(json_decode($response), JSON_PRETTY_PRINT); } curl_close($curl); ``` ```csharp C# theme={"system"} using System; using System.Net.Http; using System.Threading.Tasks; namespace CommentsDELETERequest_csharp { class CommentsDELETE { private static readonly HttpClient client = new HttpClient(); static async Task Main(string[] args) { string API_KEY = "API_KEY"; string url = "https://api.ayrshare.com/api/comments/Ut1fWU6XkqkMayHGnJZ"; client.DefaultRequestHeaders.Add("Authorization", "Bearer " + API_KEY); try { HttpResponseMessage response = await client.DeleteAsync(url); response.EnsureSuccessStatusCode(); string responseBody = await response.Content.ReadAsStringAsync(); Console.WriteLine(responseBody); } catch (HttpRequestException e) { Console.WriteLine($"Error: {e.Message}"); } } } } ``` ```json 200: Success Single Comment theme={"system"} { "status": "success", "bluesky": { "action": "delete", "status": "success", "id": "at://did:plc:d/app.bsky.feed.post/3lez", // Bluesky Social Comment ID "comment": "This is a comment" }, "facebook": { "action": "delete", "status": "success", "id": "938010233_939392023", // Facebook Social Comment ID "comment": "This is a comment" }, "instagram": { "action": "delete", "status": "success", "id": "18010439663043269", // Instagram Social Comment ID "comment": "This is a comment" }, "linkedin": { "action": "delete", "status": "success", "id": "7133271664032669696", // LinkedIn Social Comment ID "comment": "This is a comment" }, "threads": { "action": "delete", "status": "success", "id": "18064102964006231" // Threads Social Comment ID }, "tiktok": { "action": "delete", "status": "success", "commentId": "7303719953248109358", // Deprecated December 1, 2023. Use the id field instead. "id": "7303719953248109358", // TikTok Social Comment ID "comment": "This is a comment" }, "twitter": { "action": "delete", "status": "success", "id": "1633128546494459904", // Twitter Social Comment ID "comment": "This is a comment" }, "youtube": { "action": "delete", "status": "success", "id": "Ugy2m5u-LS9M29Gn3hd4AaABAg", // YouTub Social Comment ID "comment": "This is a comment" } } ``` ```json 200: Sucesss Multiple Comments theme={"system"} { "status": "success", "linkedin": [ { "action": "delete", "status": "success", "id": "7090782997972410368", // LinkedIn Social Comment ID "comment": "This is a comment" }, { "action": "delete", "status": "success", "id": "7090783025164103680", // LinkedIn Social Comment ID "comment": "This is a comment" } ], "twitter": [ { "action": "delete", "posts": [ { "action": "delete", "status": "success", "id": "1685017310942134272", // Twitter Social Comment ID "comment": "This is a comment" } ] }, { "action": "delete", "posts": [ { "action": "delete", "status": "success", "id": "1685017338184146946", // Twitter Social Comment ID "comment": "This is a comment" } ] } ], "facebook": [ { "action": "delete", "status": "success", "id": "676770944469840_644047184361660", // Facebook Social Comment ID "comment": "This is a comment" }, { "action": "delete", "status": "success", "id": "676770944469840_983782432932944", // Facebook Social Comment ID "comment": "This is a comment" } ] } ``` ```json 200: Success Hide TikTok Comment theme={"system"} { "status": "success", "tiktok": { "action": "hide", "status": "success", "id": "7303719953248109358", // TikTok Social Comment ID "comment": "This is a comment" } } ``` ```json 400: Comment id not found theme={"system"} { "action": "comments", "status": "error", "code": 219, "message": "Error getting comments. Please verify the comments are still available." } ``` # Get Comments Source: https://www.ayrshare.com/docs/apis/comments/get-comments GET /comments/:id Fetch comments on a post by Ayrshare Post ID, Social Post ID, or Comment ID, with multiplatform partial-success responses across networks. > [!NOTE] > > **Targeting X/Twitter?** Starting March 31, 2026, all X operations require your own API credentials. After linking X via OAuth, include these 2 headers in your request: > > `X-Twitter-OAuth1-Api-Key` — Your API Key (Consumer Key) > > `X-Twitter-OAuth1-Api-Secret` — Your API Key Secret (Consumer Secret) > > **One-time setup per Ayrshare account.** You create one X Developer App and reuse the same API Key and Secret across every sub-profile / end-user you link. You do *not* create a new app per customer. > > Not linked yet? See the [full setup guide](/docs/dashboard/connect-social-accounts/x-twitter-byo-keys) to connect your X account. > > Your keys are never logged or stored by Ayrshare. Get comments for a post using the Ayrshare post ID, Social Post ID, Ayrshare Comment ID, or Social Comment ID with the Comment API. Please see the [Comments Overview](/docs/apis/comments/overview) for more information on the different ID types. > [!TIP] > > If using the Ayrshare Post ID no query parameters are required. ## Additional Comment Details
    • Comment data is updated every 10 minutes for all platforms, with the exception of X. Due to restrictions imposed by the X API, the comment data for X is refreshed using an exponential backoff strategy, meaning the intervals between updates gradually increase over time.
    • On X, requesting comments for a post that is itself a reply returns the replies to that post. An X post that starts its own conversation is unchanged and still returns the replies from across that conversation.
    • In the Facebook response, comment replies to replies always have the same `parent.id`.
    • Retrieve LinkedIn replies to comments by setting the `"commentId": true` and `"searchPlatformId": true` query parameters and providing the [Social Comment ID](/docs/apis/comments/overview#comments-with-social-comment-id) in the path parameter.
    • Facebook and Instagram return up to the most recent 1,000 comments on a post. Please contact us regarding higher limits on the Enterprise plan.
    • For a TikTok post still being processed (its `id` is `"pending"`), get-comments returns a clear "still processing" error ([`code: 288`](/docs/errors/errors-ayrshare#tiktok-comment-errors), HTTP `400`) rather than a generic failure. Retry once the [`tikTokPublished` webhook](/docs/apis/post/social-networks/tiktok#tiktok-processing) fires or [/history](/docs/apis/history/overview) shows the resolved video id.
    ## Multiplatform Reads & Partial Success When you request comments with the [Ayrshare Post ID](/docs/apis/overview#ayrshare-post-id), the post may span several platforms. Ayrshare fans out one request ("leg") per platform and returns a **partial success** if some legs succeed and others fail — the healthy platforms' comment data is always returned, and each failed leg is enumerated in a top-level `errors[]` array.
    • **Some platforms succeed, some fail:** the response is HTTP `200` with `status: "partial"`. The healthy platform blocks are returned as usual, and a top-level `errors[]` array lists every failed leg with its `platform`, `status`, `code`, `message`, and `id`.
    • **Every platform fails:** the response has `status: "error"` and the full `errors[]` array. The HTTP status is mapped from the representative top-level error code. Code `485` maps to HTTP `404`; other representative codes use their own mappings.
    • **All platforms succeed:** the response is unchanged — HTTP `200`, `status: "success"`, and no `errors[]` key.
    > [!WARNING] > > **Behavior change — inspect `errors[]`, don't branch on HTTP status.** Because a multiplatform read with a failing leg now returns HTTP `200` instead of collapsing the whole response to an error, integrators should always check for the presence of a top-level `errors[]` array to detect per-platform failures rather than relying on the HTTP status code alone. ### Expired or Unavailable Instagram / Facebook Story Comments An Instagram or Facebook Story comments leg that is expired or unavailable — so its comments cannot be retrieved — surfaces in `errors[]` with code [`485`](/docs/errors/errors-ayrshare#expired-or-unavailable-story-errors). A representative Instagram message is *"Instagram Story expired or unavailable — comments/insights cannot be retrieved."* If another platform succeeds, its comments are still returned and the overall response is **HTTP `200`**. For an all-fail response, representative code `485` maps to HTTP `404`; other representative codes use their own mappings. Match on the `code` (`485`), not the exact message text. #### Example: Partial Success Response ```json 200: Partial Success theme={"system"} { "facebook": [ { "comment": "What a great comment", "commentId": "806720068141593_1849585385469876", "created": "2026-07-14T19:55:28Z", "likeCount": 12, "platform": "facebook" } ], "status": "partial", "id": "Ut2fWU6XkqkMayHGnJZ7", "lastUpdated": "2026-07-14T22:30:13.035Z", "nextUpdate": "2026-07-14T22:41:13.035Z", "errors": [ { "platform": "instagram", "status": "error", "code": 485, "message": "Instagram Story expired or unavailable — comments/insights cannot be retrieved.", "id": "17895695668004550" } ] } ``` ### X Billing and Rate-Limit Failures A comments read for X can fail on your own X Developer account — most often because it has no API credits left, or because you have hit X's rate limit. These failures return a typed error rather than an empty comment list, so an account problem is never mistaken for a post that has no comments.
    • **X returns HTTP `402`** (no API credits): Ayrshare returns [`code: 416`](/docs/errors/errors-ayrshare#x-twitter-byo-key-errors) with HTTP `402`.
    • **X returns HTTP `429`** (rate limit): Ayrshare returns [`code: 364`](/docs/errors/errors-ayrshare#x-twitter-byo-key-errors) with HTTP `429`. When X's `429` says the account is out of credits, Ayrshare returns `code: 416` instead, because waiting will not clear it.
    • **Authorization failures** return `code: 272`, and any other X failure returns `code: 219` — both with HTTP `400`.
    In a multiplatform read, the response has top-level `status: "partial"` and the failed X leg appears in `errors[]` with `status: "error"`, exactly as described above; a single-platform read returns the error at the top level. When X rejects the request outright, the failure is not cached, so the read is retried as soon as you request comments again. An X post that was deleted, or that your X credentials cannot see, still returns an empty array with no error. ## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` **Profile-Key** `string` [Profile Key](/docs/apis/overview#profile-key-format) of a User Profile. Format: `Profile-Key: PROFILE_KEY` ## Path Parameters **id** `string` *required* [Ayrshare Post ID](/docs/apis/comments/overview#comments-with-ayrshare-post-id), [Social Post ID](/docs/apis/comments/overview#comments-with-social-post-id), or [Social Comment ID](/docs/apis/comments/overview#comments-with-social-comment-id). ## Query Parameters **searchPlatformId** `boolean` (default: false) If getting comments on a post published via Ayrshare and using the [Ayrshare Post ID](/docs/apis/comments/overview#comments-with-ayrshare-post-id), do not include this field - it defaults to `false`. If getting comments using the [Social Post ID](/docs/apis/comments/overview#comments-with-social-post-id) or [Social Comment ID](/docs/apis/comments/overview#comments-with-social-comment-id), which is the ID generated by the social networks, set to `true`. **commentId** `boolean` (default: false) If getting comments using the [Social Comment ID](/docs/apis/overview#social-comment-id), which is the comment ID generated by the social networks, set to `true`. If getting comment using the Ayrshare Post ID or Social Post ID, do not include this field - it defaults to `false`. > [!NOTE] > > If using the `commentId` query parameter, you must also set `searchPlatformId` to `true`. **platform** `string` Required if `searchPlatformId` or `commentId` is `true`. When to use:
    • If using the [Social Post ID](/docs/apis/comments/overview#comments-with-social-post-id) and `"searchPlatformId": true` field. Supported platforms: `bluesky`, `facebook`, `instagram`, `linkedin`, `reddit`, `threads`, `tiktok`, `twitter`, `youtube`.
    • If using the [Social Comment ID](/docs/apis/comments/overview#comments-with-social-comment-id) and `"searchPlatformId": true` and `"commentId": true` fields. Supported platforms: `facebook`, `instagram`, `linkedin`.
    • If using the [Ayrshare Post ID](/docs/apis/comments/overview#comments-with-ayrshare-post-id), do not include this field - comments will be returned from all platforms where the original post was published.
    ## Comment GET Request Examples ```json With Ayrshare Post ID theme={"system"} // Ayrshare Post ID GET /comments/:id ``` ```json With Social Post ID theme={"system"} // Social Post ID // Platforms: facebook, instagram, linkedin, tiktok, twitter, youtube GET /comments/:id?searchPlatformId=true&platform=facebook ``` ```json With Social Comment ID theme={"system"} // Social Comment ID // Platforms: facebook, instagram, linkedin GET /comments/:id?commentId=true&searchPlatformId=true&platform=facebook ``` ```bash cURL theme={"system"} curl \ -H "Authorization: Bearer API_KEY" \ -X GET https://api.ayrshare.com/api/comments/Ut1fWU6XkqkMayHGnJZ ``` ```javascript JavaScript theme={"system"} const API_KEY = "API_KEY"; fetch("https://api.ayrshare.com/api/comments/Ut1fWU6XkqkMayHGnJZ", { method: "GET", headers: { Authorization: `Bearer ${API_KEY}`, }, }) .then((res) => res.json()) .then((json) => console.log(json)) .catch(console.error); ``` ```python Python theme={"system"} import requests headers = {'Authorization': 'Bearer API_KEY'} r = requests.get('https://api.ayrshare.com/api/comments/Ut1fWU6XkqkMayHGnJZ', headers=headers) print(r.json()) ``` ```php PHP theme={"system"} true, CURLOPT_HTTPHEADER => $headers ]); $response = curl_exec($curl); if ($response === false) { echo 'Curl error: ' . curl_error($curl); } else { echo json_encode(json_decode($response), JSON_PRETTY_PRINT); } curl_close($curl); ``` ```csharp C# theme={"system"} using System; using System.Net.Http; using System.Threading.Tasks; namespace CommentsGETRequest_csharp { class CommentsGET { private static readonly HttpClient client = new HttpClient(); static async Task Main(string[] args) { string API_KEY = "API_KEY"; string url = "https://api.ayrshare.com/api/comments/Ut1fWU6XkqkMayHGnJZ"; client.DefaultRequestHeaders.Add("Authorization", "Bearer " + API_KEY); try { HttpResponseMessage response = await client.GetAsync(url); response.EnsureSuccessStatusCode(); string responseBody = await response.Content.ReadAsStringAsync(); Console.WriteLine(responseBody); } catch (HttpRequestException e) { Console.WriteLine($"Error: {e.Message}"); } } } } ``` ```json 200: Success {2, 19, 60, 99, 141, 156, 194, 229, 280} theme={"system"} { "bluesky": [ { "comment": "Nice one this is", "commentId": "at://did:plc:62musrcyanhro2lydyhlw7ci/app.bsky.feed.post/3lf4b4j3dqy2i", "created": "2025-01-06T23:12:58.950Z", "displayName": "Ayrshare", "likeCount": 0, "platform": "bluesky", "profileImageUrl": "https://cdn.bsky.app/img/avatar/plain/did:plc:62musrcyanhro2lydyhlw7ci/bafkreiegtpqhpwgu6tww2ejsdil4ew3blmt6jh3wlq6zhanxo3wj2ib4du@jpeg", "quoteCount": 0, "replies": [], "replyCount": 0, "replyTo": "at://did:plc:n7atrjd22xgkmgwig6dzlhzd/app.bsky.feed.post/3lez7fwx45723", "repostCount": 0, "userName": "ayrshare.com" } ], "facebook": [ { // The most recent 1,000 comments retrieved. "comment": "What a great comment", "commentId": "806720068141593_1849585385469876", // Facebook Social Comment ID "commentCount": 1, // The number of replies to the comment "commentUrl": "https://www.facebook.com/106148652329/posts/184958538876/", // URL to the comment "created": "2024-02-27T19:55:28Z", "from": { // If available. Facebook determines availability based on the user's security profile and settings. "name": "John Smith", "id": "5075334022535844" }, "likeCount": 342, "platform": "facebook", "userLikes": false, "replies": [ // Facebook replies to replies have the same parent id regardless of nesting. { "comment": "Nice Comment 2", "commentId": "806720068141593_320599703877348", "commentCount": 0, "company": true, "created": "2024-02-27T19:55:40Z", "from": { "name": "Sue Me", "id": "5075334022535800" }, "likeCount": 3, "parent": { "createdTime": "2024-02-27T19:55:28+0000", "from": { "name": "John Smith", "id": "5075334022535844" }, "message": "What a great comment", "id": "806720068141593_1849585385469876" }, "platform": "facebook", "userLikes": false } ] } ], "instagram": [ { // The most recent 1,000 comments retrieved. "comment": "Love this post.", "commentId": "18030599567251077", // Instagram Social Comment ID "created": "2022-10-13T00:52:47Z", "from": { // If available. Instagram determines availability based on the user's security profile and settings. "name": "WondrousTimes", "id": "112775157855689" }, "hidden": false, // Is the comment hidden "likeCount": 1, "parentId": "17999860012861", // The Instagram Social Post ID of the parent post. Only returned when using get by ID on a reply. "platform": "instagram", "postId": "18011025266302661", // The Instagram top level post ID (original post). "replies": [ { "comment": "@ayrshare Thanks", "created": "2024-04-04T22:37:39Z", "from": { "id": "178414522127073334", "username": "ayrshare" }, "hidden": false, "id": "18023114651100610", // Instagram Social Comment Id "likeCount": 2, "parentId": "18030599567251077", "user": { "id": "17841452212702234" }, "username": "ayrshare" } ], "user": { // ID of Instagram user who created the comment. "id": "112775157855689" }, "userName": "ayrshare" // If available. Instagram determines availability based on the user's security profile and settings. } ], // Retrieve LinkedIn replies to comments by using the commentID with the Get Comments with Social Comment ID endpoint. "linkedin": [ { "comment": "New wonderful comment", "commentId": "6961077099692445688", // LinkedIn Social Comment ID "commentUrn": "urn:li:comment:(urn:li:activity:7141202289402236928,7141205176207503360)", // Social comment id from LinkedIn "created": "2022-08-04T21:55:11Z", "from": { "name": "Sue Smith", "id": "yb8P0Oq5l8", "url": "https://www.linkedin.com/in/suesmith", "description": "Co-Founder at Smiths" }, "likeCount": 34, "media": [ { "type": "image", "url": "https://media-exp1.licdn.com/dms/image/D4E2CAQH4RRMRaJSR" } ], "platform": "linkedin", "profileImageUrl": "https://media-exp1.licdn.com/dms/image/C5103AQHORT70jVfK", "userName": "suesmith" }, { // Comment from a company "comment": "Love it", "commentId": "urn:li:comment:(urn:li:activity:71860989603258818,7186100659052515329)", "commentUrn": "urn:li:comment:(urn:li:activity:71860989603258818,7186100659052515329)", "created": "2024-04-16T20:38:28Z", "founded": 2020, "from": { "name": "Ayrshare", "id": 22327643, "url": "https://www.linkedin.com/company/22327643", "description": "Social Media API" }, "likeCount": 123, "organizationType": "PRIVATELY_HELD", "platform": "linkedin", "userName": "ayrshare", "website": "https://www.ayrshare.com" } ], "reddit": [ { "comment": "Nice comment", "commentId": "kaxeilk", "created": "2023-11-27T03:09:06.000Z", "from": { "name": "suppercomment", "id": "t2_3zaxa" }, "commentUrl": "/r/test/comments/184szm7/reddit_post_title/kaxeilk/", "subreddit": "test", "ups": 1, "isSubmitter": true } ], "threads": [ { "comment": "An amazing comment!", "commentId": "17890924860234841", "commentUrl": "https://www.threads.com/@ayrshare/post/DI4pRHAto1", "created": "2025-04-25T21:43:57+0000", "hasReplies": false, "isQuotePost": false, "isReply": true, "isReplyOwnedByMe": true, "mediaType": "text_post", "parentId": "17890643139123706", "platform": "threads", "postId": "17890643139123706", "replies": [ { "comment": "sweet", "commentId": "17961516995911683", "commentUrl": "https://www.threads.com/@ayrshare/post/DI4shgspzI1", "created": "2025-04-25T22:12:22+0000", "hasReplies": false, "isQuotePost": false, "isReply": true, "isReplyOwnedByMe": true, "mediaType": "text_post", "parentId": "17890924860234841", "platform": "threads", "postId": "17890643139123706", "replyAudience": "everyone", "shortcode": "DI4shgspzI1", "userName": "ayrshare" } ], "replyAudience": "everyone", "shortcode": "DI4pRHAtoVu", "userName": "ayrshare" } ], "tiktok": [ { "comment": "The best comment", "commentId": "7260964914699764523", // TikTok Social Comment ID "created": "2023-07-28T20:12:23Z", "displayName": "John Smith", "liked": false, // Whether the user who posted the video has liked the comment. "likeCount": 24, "owner": true, // Whether the user who posted the video made the comment. "pinned": false, // If the comment is pinned "platform": "tiktok", "profileImageUrl": "https://p16-sign-va.tiktokcdn.com/tos-maliva-avt-0068/f123c4b57", "replies": [ { "comment": "One reply", "commentId": "72776074297628920", // TikTok Social Comment ID "createTime": "2023-09-11 16:34:20", "liked": false, // Whether the user who posted the video has liked the comment reply. "likes": 0, "owner": false, // Whether the user who posted the video made the comment reply. "parentCommentId": "72776073973290774", // TikTok Parent Social Comment ID "profileImageUrl": "https://p16-sign.tiktokcdn-us.com/tos-useast5-avt-0068-tx/bcc6e6778x168.jpeg", "status": "PUBLIC", "userId": "51e0cb95a8b14f0f1020aa017", "username": "ayrshare", "videoId": "7260964048362310958" // TikTok Social Post ID } ], "status": "success", "userId": "fc904564301a6d383fd19bd4982ba2682404876533c04741a8c3f270376cd3dd", "username": "helmar1066", "videoId": "7260964048362310958", // TikTok Social Post ID "visibility": "public" } ], "twitter": [ { "bookmarkCount": 2, "comment": "@ayrshare Congrats on this", "commentId": "1505595871831891068", // Twitter Social Comment ID "created": "2022-05-14T21:56:14.000Z", "description": "Eat great!", "id": "1552209331030036480", // Twitter Social Post ID "impressionCount": 134, "likeCount": 341, "name": "waffles", "platform": "twitter", "profileImageUrl": "https://pbs.twimg.com/profile_images/119426890464/lvQZPpt4_normal.png", "publicMetrics": { // X public metrics of the user who posted the comment "followersCount": 56, "followingCount": 2242, "tweetCount": 32, "listedCount": 3, "likeCount": 459, "mediaCount": 244 }, "quoteCount": 2, "referencedTweets": [ // ID of the tweet that was replied to { "type": "replied_to", "id": "187203972440121733" } ], "replyCount": 3, "replyTo": { // Who the comment was in reply to "createdAt": "2012-01-18T21:13:15.000Z", "description": "Social Media API", "id": "467793290", "location": "New York City", "name": "Ayrshare", "profileImageUrl": "https://pbs.twimg.com/profile_images/1851502252/ayrshare_normal.gif", "publicMetrics": { // X public metrics of the user who the comment was in reply to "followersCount": 46, "followingCount": 1242, "tweetCount": 12, "listedCount": 2, "likeCount": 439, "mediaCount": 144 }, "url": "https://t.co/GQgCqytA", "username": "anotherone" }, "threadNumber": 1, // If thread, number in sequence "userName": "waffles" } ], "youtube": [ { "channelUrl": "http://www.youtube.com/channel/sp0CnxiNbUU0AJtuYKZwXQ", "comment": "Two comments", "commentId": "Ugx0tiPuoUpXfrY0qvV4AaABAg", // YouTube Social Comment ID "created": "2022-05-24T19:45:49Z", "isPublic": true, // This setting indicates whether the comment, including all of its comments and comment replies, is visible to all YouTube users. "likeCount": 34, "platform": "youtube", "profileImageUrl": "https://yt3.ggpht.com/ytc/AKedOLT2hOZirBUGodAg31-QXaGPd9DmrkxR48UXAw=s48-c-k-c0x00ffffff-no-rj", "replies": [ { "comment": "amazing reply", "commentId": "UgzJFjmrrRUr6nsRWi14AaABAg.9ssbUo6Es1n9ssbVwRn2iz", "created": "2023-08-01T15:59:16Z", "likeCount": 2, "platform": "youtube", "userName": "uman", "profileImageUrl": "https://yt3.ggpht.com/ytc/AOPcoUDV_W7h3ZSEcFYrcEuXUPxVCTuF4NTw=s48-c-k-c0x00ffffff-no-rj", "channelUrl": "http://www.youtube.com/channel/sp0CnxiNbUU0AJtuYKZwXQ", "parentId": "UgzJFjmrrRUr6nsRWi14AaABAg" } ], "userName": "ayrshare" }, { "channelUrl": "http://www.youtube.com/channel/sp0CnxiNbUU0AJtuYKZwXQ", "comment": "One comment", "commentId": "Ugx0tiPuoUpXfrY0qvV4AaABAa", // YouTube Social Comment ID "created": "2022-05-24T19:00:10Z", "isPublic": true, // This setting indicates whether the comment, including all of its comments and comment replies, is visible to all YouTube users. "likeCount": 123, "platform": "youtube", "profileImageUrl": "https://yt3.ggpht.com/ytc/AAKedOLT2hOZirBUGodAg31-QXaGPd9DmrkxR48UXAw=s48-c-k-c0x00ffffff-no-rj", "userName": "ayrshare" } ], "status": "success", "id": "Ut2fWU6XkqkMayHGnJZ7", "lastUpdated": "2023-03-26T22:30:13.035Z", "nextUpdate": "2023-03-26T22:41:13.035Z", } ``` ```javascript 404: Comments Not Found theme={"system"} { "action": "post", "status": "error", "code": 186, "message": "Post ID not found. Please verify the top level ID returned from the /post endpoint is being sent.", "id": "Ut2fWU6XkqkMayHGnJZ7" } ``` # Comments API Overview Source: https://www.ayrshare.com/docs/apis/comments/overview Post, reply, get, and delete comments using the Ayrshare Post ID, Social Post ID, Ayrshare Comment ID, or Social Comment ID, plus how to disable comments. The Comments endpoints allow your users to post, reply, retrieve, and delete comments on their posts. There are four types of comment IDs: Ayrshare Post ID, Social Post ID, Ayrshare Comment ID, and Social Comment ID. Each of these IDs is used for different purposes. To receive new replies on connected Threads posts in real time, register a [`comments` webhook](/docs/apis/webhooks/actions#comments-action). ## Comment ID Types ### Comments with Ayrshare Post ID The typical flow is when you publish a post via Ayrshare, you get the Ayrshare Post ID. You can then use this [Ayrshare Post ID](/docs/apis/overview#ayrshare-post-id) to manage comments on that post. Supported platforms: Bluesky, Facebook Pages, Instagram, LinkedIn, Reddit, Threads, TikTok, Twitter, and YouTube. For example: **1. Publish a Post** Use the [/post](/docs/apis/post/post) endpoint to publish a post via Ayrshare, which can be sent to multiple social networks in a single call. In the return is the top level [Ayrshare Post ID](/docs/apis/overview#ayrshare-post-id). ```json {12} theme={"system"} { "status": "success", "errors": [], "postIds": [ { "status": "success", "id": "106638148652329_591370753547555", "postUrl": "https://www.facebook.com/106638148652329/posts/591370753547555", "platform": "facebook" } ], "id": "E4UdUr1yRbvI7qqCSlaq", // Ayrshare Post ID "refId": "9abf1426d6ce9122ef11c72bd62e59807c5cc055", "post": "What an amazing day", "validate": true } ``` **2. Post a Comment** Use the [/comments](/docs/apis/comments/post-comment) endpoint to post a comment with the Ayrshare Post ID. The comment will be automatically posted to every social network where the original post was published. For example, if you published the post to Facebook and Instagram, the comment will appear on both platforms. ```http theme={"system"} POST https://api.ayrshare.com/api/comments/:id ``` **3. Get Comments** Use the [/comments](/docs/apis/comments/get-comments) endpoint to get the comments for the post with the Ayrshare Post ID. This will return all comments from every social network where the post was published. For example, if you posted to Facebook and Instagram, you'll get comments from both platforms. ```http theme={"system"} GET https://api.ayrshare.com/api/comments/:id ``` ### Comments with Social Post ID Sometimes you may want to manage comments on a post that were not published via Ayrshare. In this case you can use the [Social Post ID](/docs/apis/overview#social-post-id), which is the post ID assigned by the social network. > [!NOTE] > > On Facebook, Instagram, and TikTok, the post must belong to your own connected account or page, for example a post you published directly in the Instagram app. These platforms do not let you get or reply to comments on other users' posts, even with the Social Post ID or Social Comment ID. On Bluesky, LinkedIn, X, and YouTube, whether you can work with comments on another user's post depends on that network's rules and the post's settings. See [Commenting on Other Users' Social Posts](/docs/help-center/technical-support/commenting-on-another-user-post). > [!NOTE] > > If the post was published via Ayrshare, use [the Ayrshare Post > ID](/docs/apis/comments/overview#comments-with-ayrshare-post-id). For example: **1. Get Post History** Start by getting the Instagram posts published outside of Ayrshare with the [Get All Post History endpoint](/docs/apis/history/history-platform). The endpoint returns a list of posts with the social `id` of the post. ```json {13} theme={"system"} { "status": "success", "posts": [ { "mediaUrl": "https://scontent.cdninstagram.com/v/t51.2885-15/7531.jpg", "permalink": "https://www.instagram.com/p/B5OBT3ygpfg/", "commentsCount": 0, "created": "2022-05-20T17:26:03Z", "likeCount": 0, "mediaProductType": "FEED", "mediaType": "IMAGE", "username": "thegoodone", "id": "17833140557332933", // Instagram Social Post ID "thumbnailUrl": "https://scontent-lga3-2.cdninstagram.com/v/t51..jpg", "post": "The #Mandalorian is on tonight instead of Friday", "postUrl": "https://www.instagram.com/p/B5OBT3ygpfg/" }, ] } ``` > [!NOTE] > > The social post `id` is also returned in the `postIds` field of the [post endpoint](/docs/apis/post/post). **2. Get Comments on a Post** Use the returned `id` to [get all the comments](/docs/apis/comments/get-comments) for a specific Instagram post using the social `id`, `searchPlatformId` set to `true`, and `platform` set to `instagram`. ```http theme={"system"} GET https://api.ayrshare.com/api/comments/17833140557332933?platform=instagram&searchPlatformId=true ``` **3. Use the Comment ID to Get Comment Details** The returned JSON from the previous step will have a `commentId` for each comment, which is the [social comment ID](/docs/apis/overview#social-comment-id). ```json {5} theme={"system"} { "instagram": [ { "comment": "What an amazing comment", "commentId": "17969247335804735", // Social Comment ID "created": "2024-11-26T11:49:00Z", "from": { "id": "103038435208332", "username": "john_smith" }, "hidden": false, "likeCount": 3, "platform": "instagram", "postId": "18231730279304333", "username": "john_smith" } ] } ``` ### Comments with Ayrshare Comment ID Manage comments on a post that were published via Ayrshare by using the [Ayrshare Comment ID](/docs/apis/overview#ayrshare-comment-id). The Ayrshare comment ID is the comment ID assigned from Ayrshare and can be found in the `commentId` field in the response when [posting a comment](/docs/apis/comments/post-comment). This is often used if you want to get details on a particular comment published via Ayrshare. ### Comments with Social Comment ID Manage comments on a post that were not published via Ayrshare by using the [Social Comment ID](/docs/apis/overview#social-comment-id). The social comment ID is the comment ID from the social network, not the Ayrshare ID, and can be found in the `commentId` field in the response when [getting comments](/docs/apis/comments/get-comments). ```http theme={"system"} GET https://api.ayrshare.com/api/comments/17969247335804735?platform=instagram&searchPlatformId=true&commentId=true ``` This is often used if you want to get details on a particular comment published outside of Ayrshare, such as LinkedIn replies to comments. ## Disable Comments You can disable comments when publishing a post using the `disableComments` field in the [/post endpoint](/docs/apis/post/post). Comments will only be disabled for Instagram, LinkedIn, and TikTok. You may also enable or disable comments on an already published post using the [update post](/docs/apis/post/update-post) endpoint and the `disableComments` field. ## Platform Limitations > [!NOTE] > > **TikTok — `DELETE /comments` is own-authored only.** [`DELETE /comments`](/docs/apis/comments/delete-comments) on TikTok only succeeds for comments authored by the authenticated TikTok account itself (your own replies). Attempting to delete a comment authored by another user returns Ayrshare `code: 328`. For moderation of third-party comments on your own TikTok videos, contact support so we can confirm what's available for your linked account type. ``` ``` # Post a Comment Source: https://www.ayrshare.com/docs/apis/comments/post-comment POST /comments Add a comment to a post with an Ayrshare Post ID or Social Post ID, including supported platforms, body parameters, and @mentions in comments. > [!NOTE] > > **Targeting X/Twitter?** Starting March 31, 2026, all X operations require your own API credentials. After linking X via OAuth, include these 2 headers in your request: > > `X-Twitter-OAuth1-Api-Key` — Your API Key (Consumer Key) > > `X-Twitter-OAuth1-Api-Secret` — Your API Key Secret (Consumer Secret) > > **One-time setup per Ayrshare account.** You create one X Developer App and reuse the same API Key and Secret across every sub-profile / end-user you link. You do *not* create a new app per customer. > > Not linked yet? See the [full setup guide](/docs/dashboard/connect-social-accounts/x-twitter-byo-keys) to connect your X account. > > Your keys are never logged or stored by Ayrshare. Add a comment to a post with an Ayrshare Post ID (posts sent via Ayrshare) or a Social Post ID (posts sent outside of Ayrshare) using the Comments API.
    • Comments added to an X Thread will be posted to the first Tweet.
    • Instagram Story comments are not yet supported by Meta.
    • See the `platforms` field below for supported platforms depending on the ID type.
    > [!NOTE] > > Facebook, Instagram, and TikTok only let you comment on posts from your own connected account or page, for example a post you published directly in the Instagram app. On these platforms you cannot comment on other users' posts, even with the Social Post ID. > > On LinkedIn and X, Ayrshare does not check who owns the post. Whether a comment on another user's post succeeds depends on that network's rules and the post's reply settings. See [Commenting on Other Users' Social Posts](/docs/help-center/technical-support/commenting-on-another-user-post). Please see the [Comments Overview](/docs/apis/comments/overview) for more information on the different ID types. Supported platforms: Bluesky, Facebook, Instagram, LinkedIn, Reddit, TikTok, X, and YouTube. ## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` **Profile-Key** `string` [Profile Key](/docs/apis/overview#profile-key-format) of a User Profile. Format: `Profile-Key: PROFILE_KEY` ## Body Parameters **id** `string` *required* Ayrshare Post ID or Social Post ID from the [/post endpoint](/docs/apis/post/post). **comment** `string` *required* Text of the new comment to add to the post. **searchPlatformId** `boolean` (default: false) Set to `true` if posting a comment using the [Social Post ID](/docs/apis/comments/overview#comments-with-social-post-id), which is the post `id` from the social networks. **platforms** `array` *required* Specify the platforms to add comments. If using an [Ayrshare Post ID](/docs/apis/comments/overview#comments-with-ayrshare-post-id) the supported platforms are: `bluesky`, `facebook`, `instagram`, `linkedin`, `reddit`, `tiktok`, `twitter`, `youtube`. If no platforms are specified, the comment will be published to all connected platforms that support comments. ```json Post a Comment with the Ayrshare Post ID theme={"system"} { "id": "Ut1fWU6XkqkMayHGnJZ", // Ayrshare Post ID "comment": "An amazing comment!", // required "platforms": [ "bluesky", "facebook", "instagram", "linkedin", "reddit", "tiktok", "twitter", "youtube" ] } ``` If using a [Social Post ID](/docs/apis/comments/overview#comments-with-social-post-id) the supported platforms are -only one platform allowed at a time : `facebook`, `instagram`, `linkedin`, `tiktok`, `twitter`. ```json Post a Comment with the Social Post ID theme={"system"} { "id": "1288899996423983105", // Social Post ID "comment": "An amazing comment!", // required "searchPlatformId": true, // required // facebook, instagram, linkedin, tiktok, twitter "platforms": ["facebook"] // Only specify one platform } ``` **mediaUrls** `array` Attach an image by providing the image URL for Facebook or X/Twitter. > [!NOTE] > > Only one image is supported in the `mediaUrls` field. Supported platforms are Facebook, LinkedIn, and X/Twitter. ```json Post a Comment with an Image theme={"system"} { "id": "Ut1fWU6XkqkMayHGnJZ", // Ayrshare Post ID "comment": "An amazing comment!", // required "platforms": ["facebook"], // Facebook, LinkedIn, or X/Twitter only "mediaUrls": ["https://img.ayrshare.com/gb.jpg"] } ``` ## @Mentions in Comments You can include @mentions directly in the `comment` text string. There is no separate parameter for mentions. Simply add `@handle` in your comment text. ```json Comment with @Mention theme={"system"} { "id": "Ut1fWU6XkqkMayHGnJZ", "comment": "Great post @CompanyName! Love this.", "platforms": ["linkedin", "twitter", "facebook"] } ``` > [!NOTE] > > **LinkedIn Mentions:** LinkedIn mentions are resolved server-side by Ayrshare. Use the [LinkedIn Search endpoint](/docs/apis/listen/search/linkedin-search) to look up the correct vanity name or member name. For organizations, use `@handle`. For members, use `@vanity_name` or `@[Full Name]`. For detailed mention syntax and limitations per platform, see: * [Bluesky mentions](/docs/apis/post/social-networks/bluesky#bluesky-mentions) * [Facebook mentions](/docs/apis/post/social-networks/facebook#facebook-page-mentions) * [Instagram mentions](/docs/apis/post/social-networks/instagram#instagram-mentions) * [LinkedIn mentions](/docs/apis/post/social-networks/linkedin#linkedin-mentions) * [Reddit mentions](/docs/apis/post/social-networks/reddit#reddit-mentions) * [Threads mentions](/docs/apis/post/social-networks/threads#threads-mentions) * [TikTok mentions](/docs/apis/post/social-networks/tiktok#tiktok-mentions) * [X/Twitter mentions](/docs/apis/post/social-networks/x-twitter#mentions) * [YouTube mentions](/docs/apis/post/social-networks/youtube#youtube-mentions) See [Post Verification](/docs/testing/post-verification) for the platform-by-platform mention rules and recommendations. ```bash cURL theme={"system"} curl \ -H "Authorization: Bearer API_KEY" \ -H 'Content-Type: application/json' \ -d '{"id": "Ut1fWU6XkqkMayHGnJZ", "platforms": ["bluesky", "facebook", "instagram", "linkedin", "reddit", "tiktok", "twitter", "youtube"], "comment": "An amazing comment!"}' \ -X POST https://api.ayrshare.com/api/comments ``` ```javascript JavaScript theme={"system"} const API_KEY = "API_KEY"; fetch("https://api.ayrshare.com/api/comments", { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${API_KEY}`, }, body: JSON.stringify({ id: "Ut1fWU6XkqkMayHGnJZ", // required platforms: ["bluesky", "facebook", "instagram", "linkedin", "reddit", "tiktok", "twitter", "youtube"], // required comment: "An amazing comment!", //required }), }) .then((res) => res.json()) .then((json) => console.log(json)) .catch(console.error); ``` ```python Python theme={"system"} import requests payload = {'id': 'Ut1fWU6XkqkMayHGnJZ', 'platforms': ['bluesky', 'facebook', 'instagram', 'linkedin', 'reddit', 'tiktok', 'twitter', 'youtube'], 'comment': 'An amazing comment!'} headers = {'Content-Type': 'application/json', 'Authorization': 'Bearer API_KEY'} r = requests.post('https://api.ayrshare.com/api/comments', json=payload, headers=headers) print(r.json()) ``` ```php PHP theme={"system"} 'Ut1fWU6XkqkMayHGnJZ', // Replace with your actual post ID 'platforms' => ['bluesky', 'facebook', 'instagram', 'linkedin', 'reddit', 'tiktok', 'twitter', 'youtube'], 'comment' => 'An amazing comment!' ]); $curl = curl_init($apiUrl); curl_setopt_array($curl, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_HTTPHEADER => $headers, CURLOPT_POSTFIELDS => $data ]); $response = curl_exec($curl); if ($response === false) { echo 'Curl error: ' . curl_error($curl); } else { echo json_encode(json_decode($response), JSON_PRETTY_PRINT); } curl_close($curl); ``` ```go Go theme={"system"} package main import ( "bytes" "encoding/json" "log" "net/http" ) func main() { message := map[string]interface{}{ "id": "Ut1fWU6XkqkMayHGnJZ", // required "platforms": []string{"bluesky", "facebook", "instagram", "linkedin", "reddit", "tiktok", "twitter", "youtube"}, // required "comment": "An amazing comment!", // required } bytesRepresentation, err := json.Marshal(message) if err != nil { log.Fatalln(err) } req, _ := http.NewRequest("POST", "https://api.ayrshare.com/api/comments", bytes.NewBuffer(bytesRepresentation)) req.Header.Add("Content-Type", "application/json; charset=UTF-8") req.Header.Add("Authorization", "Bearer API_KEY") res, err := http.DefaultClient.Do(req) if err != nil { log.Fatal("Error:", err) } res.Body.Close() } ``` ```csharp C# theme={"system"} using System; using System.Net.Http; using System.Text; using System.Threading.Tasks; namespace CommentsPOSTRequest_csharp { class CommentsPOST { private static readonly HttpClient client = new HttpClient(); static async Task Main(string[] args) { string API_KEY = "API_KEY"; string url = "https://api.ayrshare.com/api/comments"; client.DefaultRequestHeaders.Add("Authorization", "Bearer " + API_KEY); string json = "{\"id\": \"Ut1fWU6XkqkMayHGnJZ\"," + "\"platforms\": [\"bluesky\", \"facebook\", \"instagram\", \"linkedin\", \"reddit\", \"tiktok\", \"twitter\", \"youtube\"]," + "\"comment\": \"An amazing comment!\"}"; try { var content = new StringContent(json, Encoding.UTF8, "application/json"); HttpResponseMessage response = await client.PostAsync(url, content); response.EnsureSuccessStatusCode(); string responseBody = await response.Content.ReadAsStringAsync(); Console.WriteLine(responseBody); } catch (HttpRequestException e) { Console.WriteLine($"Error: {e.Message}"); } } } } ``` ```json 200: Success theme={"system"} { "status": "success", "commentID": "IUDSCfdYfFkw_dWhW9PLI", // Ayrshare Comment ID. Used for replying to a comment. "id": "Ut1fWU6XkqlMayHGnJZ", // Ayrshare Post ID "bluesky": { "status": "success", "commentId": "at://did:plc:62musrcyanhro2lydyhlw7ci/app.bsky.feed.post/3lf4b4j3dqy2i", "cid": "bafyreiaoivcafelkpzhbbjco23pguyqmy7wucuvfxaifgj34qoyp7nrhhy", "comment": "Nice one this is", "platform": "bluesky", "postUrl": "https://bsky.app/profile/ayrshare.com/post/3lf4b4j3dqy2i" }, "facebook": { "status": "success", "commentId": "358482752285927_363474831785719", // Facebook Social Comment ID "comment": "The best comment ever!", "platform": "facebook" }, "instagram": { "status": "success", "commentId": "17060111860440276", // Instagram Social Comment ID "comment": "The best comment ever!", "platform": "instagram" }, "linkedin": { "status": "success", "commentId": "urn:li:comment:(urn:li:activity:7141202289402236928,7141205176207503360)", // LinkedIn Social Comment ID "comment": "Someone's sitting in the shade today because someone planted a tree a long time ago. - Warren Buffett", "commentUrn": "urn:li:comment:(urn:li:activity:7141202289402236928,7141205176207503360)", // Social comment Id from LinkedIn "platform": "linkedin" }, "tiktok": { "status": "success", "commentId": "7260964914699764524", // TikTok Social Comment ID "comment": "The best comment ever!", "platform": "tiktok", "videoId": "7260964048362310959" }, "twitter": { "status": "success", "commentId": "1525632403262101648", // Twitter Social Comment ID "comment": "The best comment ever!", "postUrl": "https://twitter.com/ayrshare/status/1525632403262101648" }, "youtube": { "status": "success", "commentId": "UgzIEZsDQKXgHsEnwTR4AaABAa", // YouTube Social Comment ID "comment": "The best comment ever!", "platform": "youtube" }, } ``` ```json 404: Not Linked & Success theme={"system"} { "commentId": "5GizKtM9TFRFFWNDgBZ", "twitter": { "status": "success", "commentId": "1842266484032016", "comment": "Having a meeting on this", "platform": "twitter", "postUrl": "https://twitter.com/ayrshare/status/18422664840320" }, "action": "post", "status": "error", "code": 156, "message": "Instagram is not linked. Please confirm the linkage on the Social Accounts page in the dashboard.", "platform": "instagram", "id": "1w3QI6bU7KE9XyxrN0u", // Ayrshare Post ID "errors": [ { "action": "post", "status": "error", "code": 156, "message": "Facebook is not linked. Please confirm the linkage on the Social Accounts page in the dashboard.", "platform": "facebook" }, { "action": "post", "status": "error", "code": 156, "message": "Instagram is not linked. Please confirm the linkage on the Social Accounts page in the dashboard.", "platform": "instagram" } ] } ``` ```json 400: Duplicate comment theme={"system"} { "twitter": { "action": "post", "status": "error", "code": 134, "message": "The social network detected duplicate content and blocked this post. Please change the content and try again.", "post": "Having a meeting on this", "detail": "You are not allowed to create a Tweet with duplicate content." }, "code": 134, "status": "error", "id": "1w3QI6bU7KE9XyxrN", // Ayrshare Post ID "errors": [ { "action": "post", "status": "error", "code": 134, "message": "The social network detected duplicate content and blocked this post. Please change the content and try again.", "post": "Having a meeting on this", "detail": "You are not allowed to create a Tweet with duplicate content.", "id": "1w3QI6bU7KE9XyxrN", "platform": "twitter" } ] } ``` # Reply to a Comment Source: https://www.ayrshare.com/docs/apis/comments/reply-to-comment POST /comments/reply/:commentId Reply by Ayrshare or Social Comment ID on Bluesky, Facebook, Instagram, LinkedIn, TikTok, X, and YouTube, with @mentions and one image on Facebook or LinkedIn. > [!NOTE] > > **Targeting X/Twitter?** Starting March 31, 2026, all X operations require your own API credentials. After linking X via OAuth, include these 2 headers in your request: > > `X-Twitter-OAuth1-Api-Key` — Your API Key (Consumer Key) > > `X-Twitter-OAuth1-Api-Secret` — Your API Key Secret (Consumer Secret) > > **One-time setup per Ayrshare account.** You create one X Developer App and reuse the same API Key and Secret across every sub-profile / end-user you link. You do *not* create a new app per customer. > > Not linked yet? See the [full setup guide](/docs/dashboard/connect-social-accounts/x-twitter-byo-keys) to connect your X account. > > Your keys are never logged or stored by Ayrshare. Reply to a comment, i.e. comment on a comment, on either an Ayrshare made comment, using the [Ayrshare Comment ID](/docs/apis/comments/overview#comments-with-ayrshare-comment-id), or a comment made outside of Ayrshare, using the [Social Comment ID](/docs/apis/comments/overview#comments-with-social-comment-id). Most often, you will reply to a comment made through Ayrshare, but when you want to reply to a comment made outside of Ayrshare, you can use the Social Comment ID, which you get [GET /comment](/docs/apis/comments/get-comments) endpoint. > [!NOTE] > > Facebook, Instagram, and TikTok only let you reply to comments on posts from your own connected account or page. Here, a comment "made outside of Ayrshare" means one left on your own post, for example a comment a follower wrote in the Instagram app. On these platforms you cannot reply to comments on other users' posts, even with the Social Comment ID. > > On LinkedIn and X, Ayrshare does not check who owns the post. Whether a reply on another user's post succeeds depends on that network's rules and the post's reply settings. See [Commenting on Other Users' Social Posts](/docs/help-center/technical-support/commenting-on-another-user-post). Supported platforms: Bluesky, Facebook, Instagram, LinkedIn, TikTok, X, and YouTube. > [!NOTE] > > **YouTube comments older than 28 days.** Ayrshare keeps the record of a YouTube comment it sent for 28 days, to stay within the 30-day limit in YouTube's API policies. After that, the Ayrshare ID no longer finds the comment. To reply to it, use its [Social Comment ID](/docs/apis/comments/overview#comments-with-social-comment-id) (the YouTube comment `commentId`) with `"searchPlatformId": true`. ## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` **Profile-Key** `string` [Profile Key](/docs/apis/overview#profile-key-format) of a User Profile. Format: `Profile-Key: PROFILE_KEY` ## Path Parameters **commentId** `string` *required* If replying to a comment made through Ayrshare, use the [Ayrshare Comment ID](/docs/apis/comments/overview#comments-with-ayrshare-comment-id) `commentId` returned from the [POST comment endpoint](/docs/apis/comments/post-comment). For example, the Ayrshare comment ID `commentId` returned from the [POST comment endpoint](/docs/apis/comments/post-comment) is: ```json Ayrshare Comment ID Returned from POST /comment theme={"system"} { "commentId": "Ut1fWU6XkqkMayHGnJZ" } ``` If replying to a comment made outside of Ayrshare, use the [Social Comment ID](/docs/apis/comments/overview#comments-with-social-comment-id) `commentId` from the social network. For example, the social comment ID `commentId` returned from a Facebook [POST /comment](/docs/apis/comments/post-comment) or [GET /comment](/docs/apis/comments/get-comments) endpoint is: ```json Social Comment ID Returned from POST /comment theme={"system"} { "facebook": { "commentId": "8392829334_1234567890" } } ``` ## Body Parameters **platforms** `array` *required* If replying to a comment made through Ayrshare using the [Ayrshare Comment ID](/docs/apis/comments/overview#comments-with-ayrshare-comment-id), specify the platforms to post the reply. Supported platforms: `bluesky`, `facebook`, `instagram`, `linkedin`, `tiktok`, `twitter`, `youtube`. If replying to a comment made outside of Ayrshare using the [Social Comment ID](/docs/apis/comments/overview#comments-with-social-comment-id), specify the one platform to post the reply. Supported platforms: `facebook`, `instagram`, `linkedin`, `tiktok`, `twitter`. **comment** `string` *required* The reply to add to the comment. **mediaUrls** `array` Attach an image by providing the image URL. Only one image is supported. Facebook and LinkedIn only. **searchPlatformId** `boolean` (default: false) Set to `true` if replying to a comment using the [Social Comment ID](/docs/apis/comments/overview#comments-with-social-comment-id), which is the post `commentId` from the social networks. **commentUrn** `string` If replying to a LinkedIn comment with a Social Comment ID, you must also provide the full LinkedIn comment URN, which you get from the [POST /comment endpoint](/docs/apis/comments/post-comment) or [GET /comment endpoint](/docs/apis/comments/get-comments) as `commentUrn` — not the bare `commentId`. The URN has the form `urn:li:comment:(urn:li:activity:,)`. Only required for LinkedIn and `"searchPlatformId": true`. **videoId** `string` If replying to a TikTok comment with a Social Comment ID, you must also provide the TikTok video ID, which you get from the [POST /comment endpoint](/docs/apis/comments/post-comment) or [GET /comment endpoint](/docs/apis/comments/get-comments). Only required for TikTok and `"searchPlatformId": true`. **objResponse** `boolean` (default: true) Response as Object, otherwise as an Array. Default to `true`. ## @Mentions in Comments You can include @mentions directly in the `comment` text string. There is no separate parameter for mentions. Simply add `@handle` in your comment text. ```json Reply with @Mention theme={"system"} { "comment": "Thanks @CompanyName! Great insight.", "platforms": ["linkedin", "twitter", "facebook"] } ``` > [!NOTE] > > **LinkedIn Mentions:** LinkedIn mentions are resolved server-side by Ayrshare. Use the [LinkedIn Search endpoint](/docs/apis/listen/search/linkedin-search) to look up the correct vanity name or member name. For organizations, use `@handle`. For members, use `@vanity_name` or `@[Full Name]`. For detailed mention syntax and limitations per platform, see: * [Bluesky mentions](/docs/apis/post/social-networks/bluesky#bluesky-mentions) * [Facebook mentions](/docs/apis/post/social-networks/facebook#facebook-page-mentions) * [Instagram mentions](/docs/apis/post/social-networks/instagram#instagram-mentions) * [LinkedIn mentions](/docs/apis/post/social-networks/linkedin#linkedin-mentions) * [Threads mentions](/docs/apis/post/social-networks/threads#threads-mentions) * [TikTok mentions](/docs/apis/post/social-networks/tiktok#tiktok-mentions) * [X/Twitter mentions](/docs/apis/post/social-networks/x-twitter#mentions) * [YouTube mentions](/docs/apis/post/social-networks/youtube#youtube-mentions) Reply to comment is not supported on Reddit. See [Post Verification](/docs/testing/post-verification) for the platform-by-platform mention rules and recommendations. ```bash cURL theme={"system"} curl \ -H "Authorization: Bearer API_KEY" \ -H 'Content-Type: application/json' \ -d '{"platforms": ["bluesky", "facebook", "instagram", "linkedin", "twitter", "youtube"], "comment": "An amazing reply!"}' \ -X POST https://api.ayrshare.com/api/comments/reply/Ut1fWU6XkqkMayHGnJZ ``` ```javascript JavaScript theme={"system"} const API_KEY = "API_KEY"; fetch("https://api.ayrshare.com/api/comments/reply/Ut1fWU6XkqkMayHGnJZ", { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${API_KEY}`, }, body: JSON.stringify({ platforms: ["bluesky", "facebook", "instagram", "linkedin", "twitter", "youtube"], // required comment: "An amazing comment!", //required }), }) .then((res) => res.json()) .then((json) => console.log(json)) .catch(console.error); ``` ```python Python theme={"system"} import requests payload = {'platforms': ['bluesky', 'facebook', 'instagram', 'linkedin', 'twitter', 'youtube'], 'comment': 'An amazing comment!'} headers = {'Content-Type': 'application/json', 'Authorization': 'Bearer API_KEY'} r = requests.post('https://api.ayrshare.com/api/comments/reply/Ut1fWU6XkqkMayHGnJZ', json=payload, headers=headers) print(r.json()) ``` ```php PHP theme={"system"} ['bluesky', 'facebook', 'instagram', 'linkedin', 'twitter', 'youtube'], 'comment' => 'An amazing comment!' ]); $curl = curl_init($apiUrl); curl_setopt_array($curl, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_HTTPHEADER => $headers, CURLOPT_POSTFIELDS => $data ]); $response = curl_exec($curl); if ($response === false) { echo 'Curl error: ' . curl_error($curl); } else { echo json_encode(json_decode($response), JSON_PRETTY_PRINT); } curl_close($curl); ``` ```go Go theme={"system"} package main import ( "bytes" "encoding/json" "log" "net/http" ) func main() { message := map[string]interface{}{ "platforms": []string{"bluesky", "facebook", "instagram", "linkedin", "twitter", "youtube"}, // required "comment": "An amazing comment!", // required } bytesRepresentation, err := json.Marshal(message) if err != nil { log.Fatalln(err) } req, _ := http.NewRequest("POST", "https://api.ayrshare.com/api/comments/reply/Ut1fWU6XkqkMayHGnJZ", bytes.NewBuffer(bytesRepresentation)) req.Header.Add("Content-Type", "application/json; charset=UTF-8") req.Header.Add("Authorization", "Bearer API_KEY") res, err := http.DefaultClient.Do(req) if err != nil { log.Fatal("Error:", err) } res.Body.Close() } ``` ```csharp C# theme={"system"} using System; using System.Net.Http; using System.Text; using System.Threading.Tasks; namespace CommentsReplyPOSTRequest_csharp { class CommentsReply { static async Task Main(string[] args) { string API_KEY = "API_KEY"; string url = "https://api.ayrshare.com/api/comments/reply/Ut1fWU6XkqkMayHGnJZ"; using (var httpClient = new HttpClient()) { httpClient.DefaultRequestHeaders.Add("Authorization", "Bearer " + API_KEY); var json = @"{ ""platforms"": [""bluesky"", ""facebook"", ""instagram"", ""linkedin"", ""twitter"", ""youtube""], ""comment"": ""An amazing comment!"" }"; var content = new StringContent( json, Encoding.UTF8, "application/json" ); try { var response = await httpClient.PostAsync(url, content); response.EnsureSuccessStatusCode(); string responseBody = await response.Content.ReadAsStringAsync(); Console.WriteLine(responseBody); } catch (HttpRequestException e) { Console.WriteLine($"Error: {e.Message}"); } } } } } ``` ```json 200: Success theme={"system"} { "commentId": "Eh61xHheGHvAqXqGISGVa", "bluesky": { "status": "success", "commentId": "at://did:plc:62musrcyanhro2lydyhlw7ci/app.bsky.feed.post/3lf4bquqrxd2d", "cid": "bafyreie2jt5iw6rk2ekmiq6aljyiv7nj4sjucbpkamqc73tmggh5fmrqha", "comment": "Truth is ever to be found in simplicity.", "platform": "bluesky", "postUrl": "https://bsky.app/profile/ayrshare.com/post/3lf4bquqrxd2d", "sourceCommentId": "at://did:plc:62musrcyanhro2lydyhlw7ci/app.bsky.feed.post/3lf4b4j3dqy2i" }, "facebook": { "status": "success", "commentId": "729149842469573_696560159169326", "sourceCommentId": "729149842469573_738966840899770", "comment": "Truth is ever to be found in simplicity.", "platform": "facebook" }, "instagram": { "status": "success", "commentId": "18020060386920755", "sourceCommentId": "17998452434360734", "comment": "Truth is ever to be found in simplicity.", "platform": "instagram" }, "linkedin": { "status": "success", "commentId": "7140161360574713856", "sourceCommentId": "urn:li:ugcPost:7140160850354397187", "comment": "Truth is ever to be found in simplicity.", "platform": "linkedin" }, "tiktok": { "status": "success", "commentId": "7484295933044572922", "sourceCommentId": "7484407021429703422", "comment": "Truth is ever to be found in simplicity.", "platform": "tiktok", "videoId": "7484993689815715122" }, "twitter": { "status": "success", "commentId": "1734395672961441821", "sourceCommentId": "1734395509526155733", "comment": "Truth is ever to be found in simplicity.", "platform": "twitter", "postUrl": "https://twitter.com/ayrshare/status/1734395672961441821" }, "youtube": { "status": "success", "commentId": "UgxZ55j-FDri8lrTgVR4AaABAg.9yCaV71m1fo9yCaZvu7Hj4", "sourceCommentId": "UgxZ55j-FDri8lrTgVR4AaABAg", "comment": "Truth is ever to be found in simplicity.", "platform": "youtube" }, "status": "success", "id": "ytDgi5vyz36F2yshlJka" } ``` ```json 400: Unknown commentId theme={"system"} { "facebook": { "action": "post", "status": "error", "code": 215, "message": "Error posting comment. Please be sure you are posting a valid comment, the original post exists, the original post is publicly available." }, "code": 215, "status": "error", "errors": [ { "action": "post", "status": "error", "code": 215, "message": "Error posting comment. Please be sure you are posting a valid comment, the original post exists, the original post is publicly available.", "platform": "facebook" } ], "id": "5360106757501_178916202161" } ``` # Add RSS Feed Source: https://www.ayrshare.com/docs/apis/feeds/add-feed POST /feed Add an RSS, Substack, or YouTube feed to auto-post new articles to linked social accounts, with options for useFirstImage, autoHashtag, and platforms. Add a new RSS feed for automated posting of new articles. Posts will be automatically sent to your linked social accounts: Bluesky, Facebook, LinkedIn, Telegram, and Threads. Also Instagram and Pinterest if a valid image can be found in the article, and Snapchat if `useFirstImage` is `true` and an image is found — Snapchat cannot publish without media. Reddit, TikTok, YouTube, and Google Business Profile are not available for feeds — each needs a detail a feed entry can't supply, such as a subreddit, a video file, or a location. See [why only these networks](/docs/dashboard/automated-rss-feeds#why-only-these-networks). > [!WARNING] > > **X/Twitter is no longer supported for RSS feeds.** As of March 31, 2026, X requires your own API credentials for each request, which is incompatible with automated RSS posting. Passing `twitter` in `platforms` is ignored; if it is the only platform given, the request returns error `101`. Use [scheduled posts](/docs/apis/post/overview#schedule-posts) or direct API calls with your [BYO credentials](/docs/dashboard/connect-social-accounts/x-twitter-byo-keys) instead. ## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` **Profile-Key** `string` [Profile Key](/docs/apis/overview#profile-key-format) of a User Profile. Format: `Profile-Key: PROFILE_KEY` ## Body Parameters **url** `string` *required* URL of the RSS feed to add. **useFirstImage** `boolean` (default: false) Attempt to get the first or top image in the article and add it to the post. **autoHashtag** `boolean` (default: false) Automatically add up to 3 hashtags to the post text based on the most relevant keywords. **type** `string` (default: rss) Value: `rss`, `substack`, or `youtube`. **platforms** `array` Social network platforms to publish the article. If not provided, defaults to all connected platforms that are available for feeds. Available platforms: ```json theme={"system"} { "platforms": ["bluesky", "facebook", "instagram", "linkedin", "pinterest", "snapchat", "telegram", "threads"] } ``` ```javascript cURL theme={"system"} curl \ -H "Authorization: Bearer API_KEY" \ -H 'Content-Type: application/json' \ -d '{"url": "https://www.nytimes.com"}' \ -X POST https://api.ayrshare.com/api/feed ``` ```javascript JavaScript theme={"system"} const API_KEY = "API_KEY"; const url = "https:///www.nytimes.com"; fetch("https://api.ayrshare.com/api/feed", { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${API_KEY}` }, body: JSON.stringify({ url }) }) .then((res) => res.json()) .then((json) => console.log(json)) .catch(console.error); ``` ```python Python theme={"system"} import requests payload = {'url': 'https://www.nytimes.com'} headers = {'Content-Type': 'application/json', 'Authorization': 'Bearer API_KEY'} r = requests.post('https://api.ayrshare.com/api/feed', json=payload, headers=headers) print(r.json()) ``` ```php PHP theme={"system"} $curl = curl_init(); $data = array ( 'url' => 'https://www.nytimes.com' ); curl_setopt_array($curl, array( CURLOPT_URL => 'https://api.ayrshare.com/api/feed', CURLOPT_RETURNTRANSFER => true, CURLOPT_ENCODING => '', CURLOPT_MAXREDIRS => 10, CURLOPT_TIMEOUT => 0, CURLOPT_FOLLOWLOCATION => true, CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, CURLOPT_CUSTOMREQUEST => 'POST', CURLOPT_POSTFIELDS => http_build_query($data), CURLOPT_HTTPHEADER => array( 'Authorization: Bearer API_KEY' ), )); $response = curl_exec($curl); curl_close($curl); echo $response; ``` ```json 200: Success theme={"system"} { "status": "success", "id": "4HZhptaD5", "title": "Pulte's Money and Life Thoughts", "websiteLink": "https://pulte.substack.com", "feedURL": "https://pulte.substack.com/feed" } ``` ```json 400: Feed Already Exists theme={"system"} { "action": "create", "status": "error", "code": 303, "message": "The feed https://ruthreichl.substack.com/feed already exists.", "id": "TDW3oMDoI" } ``` ```json 400: Invalid URL theme={"system"} { "action": "create", "status": "error", "code": 130, "message": "Something is wrong with the RSS feed URL. Please verify it." } ``` # Delete RSS Feed Source: https://www.ayrshare.com/docs/apis/feeds/delete-feed DELETE /feed Delete a previously added RSS feed by passing the id returned when the feed was added. Includes example requests plus success and error responses. ## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` **Profile-Key** `string` [Profile Key](/docs/apis/overview#profile-key-format) of a User Profile. Format: `Profile-Key: PROFILE_KEY` ## Body Parameters **id** `string` *required* ID of the RSS Feed returned when adding. ```bash cURL theme={"system"} curl \ -H "Authorization: Bearer API_KEY" \ -H 'Content-Type: application/json' \ -d '{"id": "4HZhptaD5"}' \ -X DELETE https://api.ayrshare.com/api/feed ``` ```javascript JavaScript theme={"system"} const API_KEY = "API_KEY"; const id = "4HZhptaD5"; fetch("https://api.ayrshare.com/api/feed", { method: "DELETE", headers: { "Content-Type": "application/json", Authorization: `Bearer ${API_KEY}`, }, body: JSON.stringify({ id }), }) .then((res) => res.json()) .then((json) => console.log(json)) .catch(console.error); ``` ```python Python theme={"system"} import requests payload = {'id': '4HZhptaD5'} headers = {'Content-Type': 'application/json', 'Authorization': 'Bearer API_KEY'} r = requests.delete('https://api.ayrshare.com/api/feed', json=payload, headers=headers) print(r.json()) ``` ```php PHP theme={"system"} $curl = curl_init(); $data = array ( 'id' => '_3yhtyd88' ); curl_setopt_array($curl, array( CURLOPT_URL => 'https://api.ayrshare.com/api/feed', CURLOPT_RETURNTRANSFER => true, CURLOPT_ENCODING => '', CURLOPT_MAXREDIRS => 10, CURLOPT_TIMEOUT => 0, CURLOPT_FOLLOWLOCATION => true, CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, CURLOPT_CUSTOMREQUEST => 'DELETE', CURLOPT_POSTFIELDS => http_build_query($data), CURLOPT_HTTPHEADER => array( 'Authorization: Bearer API_KEY' ), )); $response = curl_exec($curl); curl_close($curl); echo $response; ``` ```json 200: Success theme={"system"} { "status": "success", "id": "RDFbD_fl5" } ``` ```json 400: Can't Find RSS ID theme={"system"} { "action": "delete", "status": "error", "code": 131, "message": "Something is wrong deleting the RSS feed URL. Please try again and verify the id." } ``` # Get RSS Feeds Source: https://www.ayrshare.com/docs/apis/feeds/get-feeds GET /feed Return every RSS feed registered to your account, with each feed's URL, title, link, autoHashtag and useFirstImage settings, and created and updated dates. ## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` **Profile-Key** `string` [Profile Key](/docs/apis/overview#profile-key-format) of a User Profile. Format: `Profile-Key: PROFILE_KEY` ```json 200: Success theme={"system"} { "status": "success", "feeds": [ { "autoHashtag": false, "created": "2022-05-16T23:20:03.405Z", "description": "Social Media APIs that enable you to send social media posts effortlessly", "id": "_3yhtyd88", "image": { "height": "32", "link": "https://www.ayrshare.com", "title": "Ayrshare", "width": "32", "url": "https://www.ayrshare.com/wp-content/uploads/2020/07/cropped-ayr-icon-2FKLDFB-32x32.png" }, "link": "https://www.ayrshare.com", "title": "Ayrshare", "type": "rss", "updated": "2022-05-16T23:20:17.124Z", "url": "https://www.ayrshare.com/feed/", "useFirstImage": true } ] } ``` # RSS Feeds Overview Source: https://www.ayrshare.com/docs/apis/feeds/overview Manage RSS feeds for automated posting of blog articles, YouTube videos, and podcasts, with a webhook to get notified when a new RSS feed item is ready. Manage RSS feeds for automated posting. Great for auto-posting your blog articles, YouTube videos, and podcasts. A [webhook is available to get notified](/docs/apis/webhooks/actions#feed-action) when a new RSS feed item is available. # Update RSS Feed Source: https://www.ayrshare.com/docs/apis/feeds/update-feed PUT /feed Change settings on an existing RSS feed by its id, toggling useFirstImage to add the article's top image and autoHashtag to add up to 3 relevant hashtags. ## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` **Profile-Key** `string` [Profile Key](/docs/apis/overview#profile-key-format) of a User Profile. Format: `Profile-Key: PROFILE_KEY` ## Body Parameters **id** `string` *required* The RSS feed id returned from the RSS feed POST endpoint. **useFirstImage** `boolean` Attempt to get the first or top image in the article and add it to the post. **autoHashtag** `boolean` Automatically add up to 3 hashtags to the post text based on the most relevant keywords. ```bash cURL theme={"system"} curl \ -H "Authorization: Bearer API_KEY" \ -H 'Content-Type: application/json' \ -d '{"id": "4HZhptaD5", "useFirstImage": true, "autoHashtag": true}' \ -X PUT https://api.ayrshare.com/api/feed ``` ```javascript JavaScript theme={"system"} const API_KEY = "API_KEY"; const id = "4HZhptaD5"; fetch("https://api.ayrshare.com/api/feed", { method: "PUT", headers: { "Content-Type": "application/json", "Authorization": `Bearer ${API_KEY}` }, body: JSON.stringify({ id, "useFirstImage": true, "autoHashtag": true }), }) .then((res) => res.json()) .then((json) => console.log(json)) .catch(console.err ``` ```python Python theme={"system"} import requests payload = {'id': '4HZhptaD5', 'useFirstImage': True, 'autoHashtag': True} headers = {'Content-Type': 'application/json', 'Authorization': 'Bearer API_KEY'} r = requests.put('https://api.ayrshare.com/api/feed', json=payload, headers=headers) print(r.json()) ``` ```php PHP theme={"system"} $curl = curl_init(); $data = array ( 'id' => '_3yhtyd88', 'useFirstImage' => false, 'autoHashtag' => true ); curl_setopt_array($curl, array( CURLOPT_URL => 'https://api.ayrshare.com/api/feed', CURLOPT_RETURNTRANSFER => true, CURLOPT_ENCODING => '', CURLOPT_MAXREDIRS => 10, CURLOPT_TIMEOUT => 0, CURLOPT_FOLLOWLOCATION => true, CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, CURLOPT_CUSTOMREQUEST => 'PUT', CURLOPT_POSTFIELDS => http_build_query($data), CURLOPT_HTTPHEADER => array( 'Authorization: Bearer API_KEY' ), )); $response = curl_exec($curl); curl_close($curl); echo $response; ``` ```json 200: Success theme={"system"} { "status": "success", "id": "vVYX3cgJ7" } ``` ```json 400: RSS Feed not found theme={"system"} { "action": "update", "status": "error", "code": 304, "message": "The feed with id vVYX3cgJ does not exist." } ``` # Generate Alt Text Source: https://www.ayrshare.com/docs/apis/generate/image-alt-text POST /generate/altText Create AI-generated alt text for images by passing an image URL, with optional keywords to include and a language code for the output alt text. Create AI-generated alt text for your images. Choose the language to write the alt text and keywords to include in the alt text. ## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` ## Body Parameters **url** `string` *required* Image URL of the image to create the alt text. Must start with `https://`. Supports JPEG, PNG, GIF, WEBP, and BMP. **keywords** `array` String array of keywords or phrases to be considered when generating the alt text. Typically only one or two of the keywords from the array will be used in the alt text. **lang** `string` Language to output the alt text. Use one of the available [language codes](/docs/iso-codes/language). ```bash cURL theme={"system"} curl \ -H "Authorization: Bearer API_KEY" \ -H 'Content-Type: application/json' \ -d '{"url": "https://img.ayrshare.com/012/gb.jpg"}' \ -X POST https://api.ayrshare.com/api/generate/altText ``` ```javascript JavaScript theme={"system"} const API_KEY = "API_KEY"; fetch("https://api.ayrshare.com/api/generate/altText", { method: "POST", headers: { "Content-Type": "application/json", "Authorization": `Bearer ${API_KEY}` }, body: JSON.stringify({ url: "https://img.ayrshare.com/012/gb.jpg", // required }), }) .then((res) => res.json()) .then((json) => console.log(json)) .catch(console.error); ``` ```python Python theme={"system"} import requests payload = {'url': 'https://img.ayrshare.com/012/gb.jpg'} headers = {'Content-Type': 'application/json', 'Authorization': 'Bearer API_KEY'} r = requests.post('https://api.ayrshare.com/api/generate/altText', json=payload, headers=headers) print(r.json()) ``` ```php PHP theme={"system"} $curl = curl_init(); $data = array ( "url" => "https://img.ayrshare.com/012/gb.jpg" ); curl_setopt_array($curl, array( CURLOPT_URL => 'https://api.ayrshare.com/api/generate/altText', CURLOPT_RETURNTRANSFER => true, CURLOPT_ENCODING => '', CURLOPT_MAXREDIRS => 10, CURLOPT_TIMEOUT => 0, CURLOPT_FOLLOWLOCATION => true, CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, CURLOPT_CUSTOMREQUEST => 'POST', CURLOPT_POSTFIELDS => http_build_query($data), CURLOPT_HTTPHEADER => array( 'Authorization: Bearer API_KEY', 'Accept-Encoding: gzip' ), )); $response = curl_exec($curl); curl_close($curl); echo $response; ``` ```csharp C# theme={"system"} using System; using System.Net.Http; using System.Text; using System.Threading.Tasks; using System.Text.Json; class Program { // Replace 'YOUR_API_KEY' with your actual Ayrshare API key private const string API_KEY = "YOUR_API_KEY"; private static readonly HttpClient client = new HttpClient(); static async Task Main(string[] args) { string imageUrl = "https://img.ayrshare.com/012/gb.jpg"; try { var result = await GenerateAltTextAsync(imageUrl); Console.WriteLine("Generated Alt Text:"); Console.WriteLine(JsonSerializer.Serialize(result, new JsonSerializerOptions { WriteIndented = true })); } catch (Exception e) { Console.WriteLine($"An error occurred: {e.Message}"); } } static async Task GenerateAltTextAsync(string imageUrl) { var url = "https://api.ayrshare.com/api/generate/altText"; var requestData = new { url = imageUrl }; client.DefaultRequestHeaders.Clear(); client.DefaultRequestHeaders.Add("Authorization", $"Bearer {API_KEY}"); var json = JsonSerializer.Serialize(requestData); var content = new StringContent(json, Encoding.UTF8, "application/json"); var response = await client.PostAsync(url, content); if (!response.IsSuccessStatusCode) { throw new HttpRequestException($"HTTP error! status: {response.StatusCode}"); } var responseBody = await response.Content.ReadAsStringAsync(); return JsonSerializer.Deserialize(responseBody); } } ``` ```json 200: Alt Text Generated theme={"system"} { "status": "success", "altText": "A ghostbusters vehicle driving through a field.", "url": "https://img.ayrshare.com/012/gb.jpg" } ``` ```json 500: Error with the Image File theme={"system"} { "action": "upload", "status": "error", "code": 115, "message": "An error occurred uploading your file, such as a timeout at the social network. Please try your post again with the /retryPost endpoint." } ``` # Generate API Overview Source: https://www.ayrshare.com/docs/apis/generate/overview AI utilities for social media posts: generate text, rewrite posts, transcribe videos, translate text, create alt text, and monthly token limits per plan. We offer a set of AI utilities that help with creating social media posts. > [card] [Generate Text](/docs/apis/generate/post-text) > [card] [Rewrite a Post](/docs/apis/generate/rewrite-post) > [card] [Transcribe a Video](/docs/apis/generate/transcribe-video) > [card] [Translate Post Text](/docs/apis/generate/translate-post) > [card] [Generate Alt Text](/docs/apis/generate/image-alt-text) > [card] [Sentiment Analysis](/docs/apis/generate/sentiment) ## Token Limits For every API call made to AI endpoints, a certain number of "tokens" are used. These tokens are calculated based on the data you send and receive. Every month, your Max Pack and overall Ayrshare account are allocated a total of 300,000 tokens for Business plans, 100,000 for Premium Plus plans, and 50,000 for Premium plans. This allocation refreshes on the first day of each month. To see how many tokens a specific API call used, as well as your total usage for the month, please check the HTTP response. # Generate Post Text Source: https://www.ayrshare.com/docs/apis/generate/post-text POST /generate/post Generate a new social post using AI from text or image URLs, with options to add hashtags and emojis or keep the post to 280 characters for Twitter. Generate a new social post using AI. You can either provide text or images to create the social media post. [Token limits](/docs/apis/generate/overview#token-limits) applicable. ## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` ## Body Parameters **text** `string` *required* Description of what the post should be about. For example: "A shoe sale happening Monday with specials on red, blue, and yellow shoes". Not required if `mediaUrls` parameter is included. **mediaUrls** `array` An array of image URLs to base the post text on. Required if `text` parameter not included. **hashtags** `boolean` (default: true) Add hashtags to the post. **emojis** `boolean` (default: false) Add emojis to the post. **twitter** `boolean` (default: false) Construct a post with 280 or few characters. ```bash cURL theme={"system"} curl \ -H "Authorization: Bearer API_KEY" \ -H 'Content-Type: application/json' \ -d '{"text": "This beautiful new shoe is on sale now. It comes in red, blue, or purple. Check it out today."}' \ -X POST https://api.ayrshare.com/api/generate/post ``` ```javascript JavaScript theme={"system"} const API_KEY = "API_KEY"; fetch("https://api.ayrshare.com/api/generate/post", { method: "POST", headers: { "Content-Type": "application/json", "Authorization": `Bearer ${API_KEY}` }, body: JSON.stringify({ text: "This beautiful new shoe is on sale now. It comes in red, blue, or purple. Check it out today.", // required }), }) .then((res) => res.json()) .then((json) => console.log(json)) .catch(console.error); ``` ```python Python theme={"system"} import requests payload = {'text': 'This beautiful new shoe is on sale now. It comes in red, blue, or purple. Check it out today.'} headers = {'Content-Type': 'application/json', 'Authorization': 'Bearer API_KEY'} r = requests.post('https://api.ayrshare.com/api/generate/post', json=payload, headers=headers) print(r.json()) ``` ```php PHP theme={"system"} "This beautiful new shoe is on sale now. It comes in red, blue, or purple. Check it out today." ); curl_setopt_array($curl, array( CURLOPT_URL => 'https://api.ayrshare.com/api/generate/post', CURLOPT_RETURNTRANSFER => true, CURLOPT_ENCODING => '', CURLOPT_MAXREDIRS => 10, CURLOPT_TIMEOUT => 0, CURLOPT_FOLLOWLOCATION => true, CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, CURLOPT_CUSTOMREQUEST => 'POST', CURLOPT_POSTFIELDS => http_build_query($data), CURLOPT_HTTPHEADER => array( 'Authorization: Bearer API_KEY', 'Accept-Encoding: gzip' ), )); $response = curl_exec($curl); curl_close($curl); echo $response; ``` ```json 200: Text theme={"system"} { "status": "success", "post": "🎉🛍️ Introducing the stunning new shoes that's now on sale! You can choose from three different colors – red, blue, or purple – and trust us, they're all amazing! Don't miss out on this beauty and grab yours today! 🙌 #newshoe #onSale #fashionable #colormebeautiful 🌈", "usage": 98, // Tokens used for this call "totalMonthlyUsage": 733 // Total tokens used for the month } ``` ```json 200: Image theme={"system"} { "status": "success", "post": ["Ventured out on a serene walk today—a path less traveled but surrounded by the lush vibrancy of nature's finest. The perfect spot to reconnect with the earth and oneself. Where do your paths lead you?\n\n#NatureWalks #ExploreOutdoors #PeacefulPath"], "mediaUrls": ["https://upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Gfp-wisconsin-madison-the-nature-boardwalk.jpg/2560px-Gfp-wisconsin-madison-the-nature-boardwalk.jpg", "https://upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Gfp-wisconsin-madison-the-nature-boardwalk.jpg/2560px-Gfp-wisconsin-madison-the-nature-boardwalk.jpg"], "usage": 2295, "totalMonthlyUsage": 6987 } ``` ```json 400: Bad Request theme={"system"} { "action": "request", "status": "error", "code": 101, "message": "Missing or incorrect parameters. Please verify with the docs. .../ayrshare.com/rest-api/endpoints" } ``` # Rewrite Post Source: https://www.ayrshare.com/docs/apis/generate/rewrite-post POST /generate/rewrite Generate up to five AI rewrites of a social media post, with options to add emojis or hashtags and to keep each version to 280 characters or fewer. Generate variations of a social media post using AI. [Token limits](/docs/apis/generate/overview#token-limits) applicable. ## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` ## Body Parameters **post** `string` *required* The post text to be rewritten. **emojis** `string` (default: false) Add emojis to the post. **hashtags** `string` (default: false) Add hashtags to the post. **twitter** `string` (default: false) Construct a post 280 or fewer characters. **rewrites** `string` (default: 5) Number of rewrites. Min: 1 and Max: 5. ```bash cURL theme={"system"} curl \ -H "Authorization: Bearer API_KEY" \ -H 'Content-Type: application/json' \ -d '{"post": "Kali melts the heart, even when the rest of the day is freezing. Happy International Polar Bear Day to the largest land carnivore and the biggest, furriest part of the Zoo’s bear community! \nOur resident polar bear Kali (pronounced “Cully”) is a wild-born bear that was born off of the northwest coast of Alaska. He was named by the people of the Native Village of Point Lay, who rescued him. \"Kali\" is the Inupiaq name for Point Lay. Eventually, the U.S. Fish and Wildlife"}' \ -X POST https://api.ayrshare.com/api/generate/rewrite ``` ```javascript JavaScript theme={"system"} const API_KEY = "API_KEY"; fetch("https://api.ayrshare.com/api/generate/rewrite", { method: "POST", headers: { "Content-Type": "application/json", "Authorization": `Bearer ${API_KEY}` }, body: JSON.stringify({ post: "Kali melts the heart, even when the rest of the day is freezing. Happy International Polar Bear Day to the largest land carnivore and the biggest, furriest part of the Zoo’s bear community! \nOur resident polar bear Kali (pronounced “Cully”) is a wild-born bear that was born off of the northwest coast of Alaska. He was named by the people of the Native Village of Point Lay, who rescued him. \"Kali\" is the Inupiaq name for Point Lay. Eventually, the U.S. Fish and Wildlife", // required }), }) .then((res) => res.json()) .then((json) => console.log(json)) .catch(console.error); ``` ```python Python theme={"system"} import requests payload = {'post': 'Kali melts the heart, even when the rest of the day is freezing. Happy International Polar Bear Day to the largest land carnivore and the biggest, furriest part of the Zoo’s bear community! \nOur resident polar bear Kali (pronounced “Cully”) is a wild-born bear that was born off of the northwest coast of Alaska. He was named by the people of the Native Village of Point Lay, who rescued him. \"Kali\" is the Inupiaq name for Point Lay. Eventually, the U.S. Fish and Wildlife'} headers = {'Content-Type': 'application/json', 'Authorization': 'Bearer API_KEY'} r = requests.post('https://api.ayrshare.com/api/generate/rewrite', json=payload, headers=headers) print(r.json()) ``` ```php PHP theme={"system"} "Kali melts the heart, even when the rest of the day is freezing. Happy International Polar Bear Day to the largest land carnivore and the biggest, furriest part of the Zoo’s bear community! \nOur resident polar bear Kali (pronounced “Cully”) is a wild-born bear that was born off of the northwest coast of Alaska. He was named by the people of the Native Village of Point Lay, who rescued him. \"Kali\" is the Inupiaq name for Point Lay. Eventually, the U.S. Fish and Wildlife" ); curl_setopt_array($curl, array( CURLOPT_URL => 'https://api.ayrshare.com/api/generate/rewrite', CURLOPT_RETURNTRANSFER => true, CURLOPT_ENCODING => '', CURLOPT_MAXREDIRS => 10, CURLOPT_TIMEOUT => 0, CURLOPT_FOLLOWLOCATION => true, CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, CURLOPT_CUSTOMREQUEST => 'POST', CURLOPT_POSTFIELDS => http_build_query($data), CURLOPT_HTTPHEADER => array( 'Authorization: Bearer API_KEY', 'Accept-Encoding: gzip' ), )); $response = curl_exec($curl); curl_close($curl); echo $response; ``` ```json 200: Success theme={"system"} { "status": "success", "post": "Kali melts the heart, even when the rest of the day is freezing. Happy International Polar Bear Day to the largest land carnivore and the biggest, furriest part of the Zoo’s bear community! \nOur resident polar bear Kali (pronounced “Cully”) is a wild-born bear that was born off of the northwest coast of Alaska. He was named by the people of the Native Village of Point Lay, who rescued him. \"Kali\" is the Inupiaq name for Point Lay. Eventually, the U.S. Fish and Wildlife", "rewrites": [ "❄️🐻 Kali, the lovable polar bear, warms our hearts on this #InternationalPolarBearDay. Fun fact: he was named after Inupiaq village, Point Lay! #FurryZooCommunity 🐾", "Happy #InternationalPolarBearDay to Kali, the largest land carnivore of the Zoo community! Even on freezing days, Kali steals our hearts with his adorable antics. 🐻❤️", "Meet Kali, the wild-born polar bear with a heart-melting charm. 🐾❄️ Sending love on this #InternationalPolarBearDay to the Zoo's fluffiest resident. 🐻❤️", "On #InternationalPolarBearDay, we celebrate Kali, the furry wonder from the largest land carnivore family of the Zoo. 🐾🐻 He was named after an Inupiaq village, Point Lay!🌟", "Kali, the Zoo's beloved polar bear, steals our hearts with his playful spirit, as we celebrate #InternationalPolarBearDay. 🐻❤️ Born off the Alaskan coast, he's a true warrior! 💪🌟" ], "usage": 98, // Tokens used for this call "totalMonthlyUsage": 733 // Total tokens used for the month } ``` ```json 400: Missing parameters theme={"system"} { "action": "request", "status": "error", "code": 101, "message": "Missing or incorrect parameters. Please verify with the docs. .../ayrshare.com/rest-api/endpoints" } ``` # Sentiment Analysis Source: https://www.ayrshare.com/docs/apis/generate/sentiment POST /generate/sentiment Run sentiment analysis on a social media post or comment to learn if the text is positive, negative, or neutral, with recommendations to improve it. Generate a sentiment analysis on a social media post or comment to understand if the text is positive, negative, or neutral and recommendations on improving the text for a more positive reaction. ## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` ## Body Parameters **text** `string` *required* The string of text to generate the sentiment analysis on. ### Request Examples ```bash cURL theme={"system"} curl \ -H "Authorization: Bearer API_KEY" \ -H 'Content-Type: application/json' \ -d '{"text": "These shoes aren't the best. The color has faded and they are uncomfortable to walk in."}' \ -X POST https://api.ayrshare.com/api/generate/sentiment ``` ```javascript JavaScript theme={"system"} const API_KEY = 'YOUR_API_KEY'; const analyzeTextSentiment = async (text) => { try { const response = await fetch('https://api.ayrshare.com/api/generate/sentiment', { method: 'POST', headers: { 'Authorization': `Bearer ${API_KEY}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ text }) }); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const data = await response.json(); console.log('Sentiment Analysis Result:', data); return data; } catch (error) { console.error('Error analyzing sentiment:', error.message); throw error; } }; ``` ```python Python theme={"system"} import requests import json API_KEY = 'YOUR_API_KEY' def analyze_text_sentiment(text): url = 'https://api.ayrshare.com/api/generate/sentiment' headers = { 'Authorization': f'Bearer {API_KEY}', 'Content-Type': 'application/json' } data = {'text': text} try: response = requests.post(url, headers=headers, json=data) response.raise_for_status() # Raises a HTTPError if the status is 4xx, 5xx result = response.json() print('Sentiment Analysis Result:', json.dumps(result, indent=2)) return result except requests.exceptions.RequestException as e: print('Error analyzing sentiment:', e) raise ``` ```php PHP theme={"system"} $text]); $ch = curl_init($url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, $data); curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); if (curl_errno($ch)) { echo 'Error analyzing sentiment: ' . curl_error($ch); return null; } curl_close($ch); if ($httpCode >= 200 && $httpCode < 300) { $result = json_decode($response, true); echo "Sentiment Analysis Result:\n"; print_r($result); return $result; } else { echo "HTTP Error: " . $httpCode . "\n"; echo "Response: " . $response . "\n"; return null; } } ``` ```csharp C# theme={"system"} using System; using System.Net.Http; using System.Text; using System.Threading.Tasks; using System.Text.Json; class Program { private const string API_KEY = "YOUR_API_KEY"; private static readonly HttpClient client = new HttpClient(); static async Task Main(string[] args) { string textToAnalyze = "These shoes aren't the best. The color has faded and they are uncomfortable to walk in."; try { var result = await AnalyzeTextSentimentAsync(textToAnalyze); Console.WriteLine("Sentiment Analysis Result:"); Console.WriteLine(JsonSerializer.Serialize(result, new JsonSerializerOptions { WriteIndented = true })); } catch (Exception e) { Console.WriteLine($"An error occurred: {e.Message}"); } } static async Task AnalyzeTextSentimentAsync(string text) { var url = "https://api.ayrshare.com/api/generate/sentiment"; var requestData = new { text = text }; client.DefaultRequestHeaders.Clear(); client.DefaultRequestHeaders.Add("Authorization", $"Bearer {API_KEY}"); var json = JsonSerializer.Serialize(requestData); var content = new StringContent(json, Encoding.UTF8, "application/json"); var response = await client.PostAsync(url, content); if (!response.IsSuccessStatusCode) { throw new HttpRequestException($"HTTP error! status: {response.StatusCode}"); } var responseBody = await response.Content.ReadAsStringAsync(); return JsonSerializer.Deserialize(responseBody); } } ``` ```json 200: Success theme={"system"} { "status": "success", "text": "These shoes aren't the best. The color has faded and they are uncomfortable to walk in.", "sentimentAnalysis": { "sentiment": "negative", // positive, negative, or neutral "opportunities": [ "Improve color durability", "Enhance comfort level" ], "recommendations": [ { "opportunity": "Improve color durability", "recommendation": "Investigate and use higher-quality dyes or materials to prevent fading over time." }, { "opportunity": "Enhance comfort level", "recommendation": "Review and possibly redesign the shoe's insole and cushioning to provide better support and comfort for walking." } ] } } ``` ```json 400: Error theme={"system"} { "action": "request", "status": "error", "code": 101, "message": "Missing or incorrect parameters. Please verify with the docs. .../ayrshare.com/rest-api/endpoints" } ``` # Transcribe a Video Source: https://www.ayrshare.com/docs/apis/generate/transcribe-video POST /generate/transcription Generate a transcription and title of a video uploaded to Ayrshare using AI, then use them to create a post summary or a YouTube title. Max 10 minutes. Generate a transcription and title of a video file using AI. This transcription can then be used in the [/generate/post](/docs/apis/generate/post-text) to create a social media summary of the video. For [YouTube](/docs/apis/post/social-networks/youtube), which requires a title, the generated title can be used. > [!WARNING] > > The video must have been previously uploaded to Ayrshare with [/media](/docs/apis/media/overview). > Videos not hosted by Ayrshare will be rejected. > > Maximum video file size is 500GB and maximum duration is 10 minutes. ### Transcribe Video Guide [Transcribe a Video](https://www.youtube.com/embed/d1JBbHrxmls) ## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` ## Body Parameters **videoUrl** `string` *required* URL encoded video URL. The video must be hosted by Ayrshare. ```bash cURL theme={"system"} curl \ -H "Authorization: Bearer API_KEY" \ -H 'Content-Type: application/json' \ -d '{"videoUrl": "https://img.ayrshare.com/random/landscape5.mp4"}' \ -X POST https://api.ayrshare.com/api/generate/transcription ``` ```javascript JavaScript theme={"system"} const API_KEY = "API_KEY"; fetch("https://api.ayrshare.com/api/generate/transcription", { method: "POST", headers: { "Content-Type": "application/json", "Authorization": `Bearer ${API_KEY}` }, body: JSON.stringify({ videoUrl: "https://img.ayrshare.com/random/landscape5.mp4", // required }), }) .then((res) => res.json()) .then((json) => console.log(json)) .catch(console.error); ``` ```python Python theme={"system"} import requests payload = {'videoUrl': 'https://img.ayrshare.com/random/landscape5.mp4'} headers = {'Content-Type': 'application/json', 'Authorization': 'Bearer API_KEY'} r = requests.post('https://api.ayrshare.com/api/generate/transcription', json=payload, headers=headers) print(r.json()) ``` ```php PHP theme={"system"} "https://img.ayrshare.com/random/landscape5.mp4" ); curl_setopt_array($curl, array( CURLOPT_URL => 'https://api.ayrshare.com/api/generate/transcription', CURLOPT_RETURNTRANSFER => true, CURLOPT_ENCODING => '', CURLOPT_MAXREDIRS => 10, CURLOPT_TIMEOUT => 0, CURLOPT_FOLLOWLOCATION => true, CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, CURLOPT_CUSTOMREQUEST => 'POST', CURLOPT_POSTFIELDS => http_build_query($data), CURLOPT_HTTPHEADER => array( 'Authorization: Bearer API_KEY', 'Accept-Encoding: gzip' ), )); $response = curl_exec($curl); curl_close($curl); echo $response; ``` ```json 200: Success theme={"system"} { "status": "success", "transcript": "This is your last chance. After this, there is no turning back. You take the blue pill the story ends you wake up in your bed and believe whatever you want to be. You take the red pill you stay in Wonderland. And I show you how deep the rabbit hole goes.", "transcriptArray": [ "This is your last chance.", "After this, there is no turning back.", "You take the blue pill the story ends you wake up in your bed and believe whatever you want to be. You take the red pill you stay in Wonderland.", "And I show you how deep the rabbit hole goes." ], "videoTitle": "Choose Wisely: Blue Pill or Red Pill Adventure Awaits!", "wordCount": 52 } ``` # Translate Post Source: https://www.ayrshare.com/docs/apis/generate/translate-post POST /generate/translate Translate post text into over 100 languages using AI. The current language is automatically detected and translated to the language code you specify. Translate text for a post to over 100 different [languages](/docs/iso-codes/language) using AI. The current language is automatically detected and translated to the specified language. ## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` ## Body Parameters **text** `string` *required* Text to be translated. **lang** `string` *required* [Language code](/docs/iso-codes/language) to translate the text. ```json 200: Success theme={"system"} { "status": "success", "translatedText": "Nos vamos a las carreras", "originalText": "Off we go to the races", "language": "es" } ``` ```json 400: Bad Language Submitted theme={"system"} { "action": "request", "status": "error", "code": 101, "message": "Missing or incorrect parameters. Please verify with the docs. .../ayrshare.com/rest-api/endpoints" } ``` # Ads Source: https://www.ayrshare.com/docs/apis/graphql/ads Manage boosted ads through the Ayrshare GraphQL API with one set of operations that takes the ads platform as an argument. The ads operations take the ads platform as an argument, so the same operation works on every ads platform Ayrshare supports. Today that is `FACEBOOK` and `INSTAGRAM`. Each operation calls the matching [REST ads endpoint](/docs/apis/ads/overview) and returns its response unchanged. Authentication and limits are the same as REST. A failed call returns a GraphQL error: `extensions.status` carries the REST status, and `extensions.code` the Ayrshare error code when the REST error has one. See [Errors](/docs/apis/graphql/errors). Ads operations need the Ads add-on. Without it, every call returns an error with `extensions.status` `403` and `extensions.code` `399`. ## Operations | Operation | Type | REST endpoint | | - | - | - | | `adAccounts` | Query | `GET /ads/{platform}/accounts` | | `adTargetingSearch` | Query | `GET /ads/{platform}/interests`, `/regions` or `/cities` | | `ads` | Query | `GET /ads/{platform}/ads` | | `adHistory` | Query | `GET /ads/{platform}/history` | | `updateAd` | Mutation | `PUT /ads/{platform}/ads` | | `boostPost` | Mutation | `POST /ads/{platform}/boost` | Every operation also takes an optional `profileKey` to act on a User Profile. All of them return the REST response as JSON, so they take no field selection. ## List ad accounts Start here. Use the `accountId` values in the response wherever an operation asks for `accountId`. ```graphql theme={"system"} query { adAccounts(platform: FACEBOOK, limit: 10) } ``` ## Search targeting options `kind` picks the search: `INTERESTS` for audience targeting, `REGIONS` or `CITIES` for locations. Each kind takes only its own arguments. For example, `countryCode` and `regionId` work only with `CITIES`, and `INTERESTS` needs `search`. ```graphql theme={"system"} query { adTargetingSearch(platform: INSTAGRAM, kind: INTERESTS, search: "running") } ``` ```graphql theme={"system"} query { adTargetingSearch(platform: FACEBOOK, kind: CITIES, search: "Austin", countryCode: "US") } ``` ## List ads Pass at least one of `campaignId`, `accountId`, `adId`, `postId` or `socialPostId`. `socialPostId` is the network's own post id, for posts not published through Ayrshare. ```graphql theme={"system"} query { ads(platform: FACEBOOK, accountId: "act_1234567890") } ``` ## See what an ad has cost ```graphql theme={"system"} query { adHistory(platform: FACEBOOK, startDate: "2026-09-01", endDate: "2026-09-30") } ``` ## Pause or resume an ad `updateAd` changes real ad spend. `PAUSED` stops spending, `ACTIVE` resumes it, and `DELETED` or `ARCHIVED` retires the ad. The same status is also set on the ad's ad set and campaign, so any other ads in that ad set or campaign change with it. A campaign or ad set the boost was attached to with `attachTo` keeps its own status, and the ad runs only while the campaign and ad set above it are active. ```graphql theme={"system"} mutation { updateAd(platform: FACEBOOK, adId: "120210000000000000", status: PAUSED) } ``` ## Boost a post `boostPost` spends real money from the ad account. It turns a published post into an ad. * Pass `postId` (the Ayrshare post id) or `socialPostId` (the network's own post id). * Get `accountId` from `adAccounts`. * `budget.amount` is in the ad account's currency. Leave `budget` out only when `attachTo.adSetId` adds the ad to an ad set you already run, which keeps its own budget. Leave `targeting` out then too: the ad set keeps its own. See [Boost into an Existing Campaign or Ad Set](/docs/apis/ads/facebook/boost-post#attach-to). * `schedule`, `targeting`, `callToAction`, `attachTo` and `status` are optional. If you leave out `status` and `schedule`, the ad is **active, starts now and has no end date**, so it keeps spending until you pause it. Set `status: PAUSED` or `schedule.end` to limit it. * Put settings that only one platform has under `platformOptions`, keyed by platform. On Facebook and Instagram, `adName` and `bidAmount` are **required** there, for example `platformOptions: { facebook: { adName: "Autumn sale", bidAmount: 2 } }`. Without them the boost returns a missing-fields error. With `attachTo.adSetId`, leave `bidAmount` out: the ad set keeps its own bid. * On Facebook and Instagram, Meta refuses the ad until the ad account has a payment method attached and, in the EU, EEA and Switzerland, personalized ads are turned on for the account linked to the Page. See [Boost Post](/docs/apis/ads/facebook/boost-post). ```graphql theme={"system"} mutation { boostPost( platform: FACEBOOK accountId: "act_1234567890" postId: "RNlSOkyxl9G3EDhlPuCK" goal: "engagement" budget: { amount: 20, type: DAILY } schedule: { start: "2026-10-10T09:00:00Z", end: "2026-10-17T09:00:00Z" } targeting: { ageMin: 25, ageMax: 45, locations: { countries: ["US"] } } platformOptions: { facebook: { adName: "Autumn sale", bidAmount: 2 } } idempotencyKey: "4f9c2a7e-1b3d-4c8e-9a6f-2d5b7e0c1a34" ) } ``` Some arguments take more than one form, so they use the `JSON` type: * `accountId` takes a string or a number. * The `schedule` dates take an ISO 8601 date or date-time, such as `"2026-10-10T09:00:00Z"`. Use that form rather than a number. `schedule.end` also takes `0` for no end date. * Location keys take one value or a list. * `targeting.interests` takes ids as strings or numbers. * `platformOptions` takes an object. > [!TIP] > > Send a new `idempotencyKey`, such as a UUID, with each boost. If a call times out or fails, retry with the same key and the same arguments. > > * If the first boost finished, the retry returns its response instead of creating and paying for a second ad. > * If the first boost is still in progress, the retry returns an error with `extensions.status` `409`. Wait, then retry with the same key. Do not switch to a new key. > * Reusing a key with different arguments also returns `409`. To stop the ad spending, call `updateAd` with `status: PAUSED`. `boostFacebookPost` and `instagramBoostPost` still work. The per-platform operations, such as `facebookAdAccounts` and `instagramAds`, still work and are unchanged. # GraphQL Errors Source: https://www.ayrshare.com/docs/apis/graphql/errors Learn why resolver errors return HTTP 200, how to read the errors array and extensions.status, keep partial data, and retry safely with an idempotencyKey. ## Resolver errors arrive with HTTP 200 This is the single most important thing to know before you write error handling. Following the GraphQL over HTTP specification, a request that reaches our resolvers returns **HTTP 200 even when an operation failed**. The failure is reported inside the response body, not by the status code. So this check does not work: ```javascript theme={"system"} // WRONG - will treat a failed request as a success if (response.ok) { return "everything worked"; } ``` Check the body instead. A few transport errors, such as 415 for an unsupported `Content-Type`, have an empty body, so check that the response is JSON before parsing it: ```javascript theme={"system"} if (!response.headers.get("content-type")?.includes("json")) { throw new Error(`GraphQL request failed with HTTP ${response.status}`); } const result = await response.json(); if (result.errors) { for (const error of result.errors) { // Errors caught before execution have no extensions, so read it safely. console.log(error.message, error.extensions?.status); } } ``` Some failures are caught before anything runs. None of them reaches a resolver or uses an API call:
    • A malformed request returns an error status: **HTTP 400** for invalid JSON or a missing `query`, **413** for a body over 64 KB, **405** for a method other than `POST`, and **415** for an unsupported `Content-Type`.
    • A query that is not valid for the schema returns **HTTP 400** only if your request sends `Accept: application/graphql-response+json`. This covers a syntax error, an unknown field, a wrong argument type, an invalid enum value, and a query over the [query limits](/docs/apis/graphql/limits#query-limits). If you send `Accept: application/json`, or no `Accept` header, the same error returns **HTTP 200**. Either way, the body has an `errors` array and no `data`.
    Once execution begins, resolver-level input validation and API failures — including authentication, authorization, upstream rate limits, and upstream 5xx responses — return HTTP 200 with an `errors` array. ## The error shape Every resolver error carries its HTTP-like API status in `extensions`: ```json theme={"system"} { "data": { "postHistory": null }, "errors": [ { "message": "Rate limit exceeded", "path": ["postHistory"], "extensions": { "status": 429, "retryAfter": 7 } } ] } ```
    • `message` — a human-readable description. We remove API keys and other credentials from resolver error messages, but an error about a value you sent, such as a variable of the wrong type, can repeat that value. Treat `message` as possibly sensitive before you log it.
    • `path` — which field in your query failed. With several fields in one request, this is how you tell which.
    • `extensions.status` — the HTTP status the equivalent REST call would have returned. Switch on this.
    • `extensions.code` — the numeric Ayrshare error code, when the underlying endpoint supplies one.
    • `extensions.retryAfter` — present on upstream quota or rate-limit 429s when a retry delay is available. It is not present on the per-request dispatch-budget 429.
    Use `extensions.code` with the [error code reference](/docs/errors/overview), or pass it to `explainError`. ## Partial success If a request asks for several things and only some succeed, you get **both** `data` and `errors`: ```graphql theme={"system"} { history: postHistory(lastDays: 7) analytics: socialAnalytics(platforms: [INSTAGRAM]) } ``` ```json theme={"system"} { "data": { "history": { "history": [{ "id": "RkQ8uXg2jT7bNd1Wq0Ya", "status": "success", "post": "Hello world" }], "refId": "9d2a7c41f0b8e6d35a1c", "count": 1, "lastUpdated": "2026-09-24T12:00:00.000Z", "nextUpdate": "2026-09-24T12:00:00.000Z" }, "analytics": null }, "errors": [ { "message": "Rate limit exceeded", "path": ["analytics"], "extensions": { "status": 429 } } ] } ``` `history` succeeded and its data is usable. Only `analytics` needs retrying. Do not discard the whole response because `errors` is present — that would throw away data you have already been billed for. ## Partial success on multi-network posts A post to several networks can succeed on some and fail on others. This is **not** reported as a GraphQL error, because the operation itself worked — it did exactly what you asked and is telling you what happened. The result separates the three outcomes: ```graphql theme={"system"} mutation { createPost(input: { post: "Hi" platforms: [FACEBOOK, INSTAGRAM] idempotencyKey: "multi-network-hi-001" }) { status postIds { platform postUrl } errors { platform message code } blocked { platform message } } } ```
    • `postIds` — networks that published, with their post URLs. Successes only.
    • `errors` — networks that failed, with the reason.
    • `blocked` — networks stopped before an attempt, for example by the per-network circuit breaker after repeated deterministic failures. These are separate from provider attempts that returned errors.
    `status` is `"error"` if **any** network failed, so it is not a reliable signal that nothing published. Read `postIds` to find out what did. ## Retries **The GraphQL API never retries automatically.** If a request fails, nothing was silently attempted again on your behalf. This matters most for mutations. `createPost` exposes `idempotencyKey`: generate a unique key before the first attempt, then reuse the same key and identical payload for every retry. A settled retry replays the original result instead of repeating the write. Do not switch to a new key after an ambiguous response. `boostFacebookPost` and `instagramBoostPost` also take an `idempotencyKey`, and it protects real money. A retry with the same key and the same arguments returns the original result, marked `idempotentReplayed: true`, instead of creating and paying for a second ad. One key covers your whole account on both Meta platforms, so never reuse a Facebook boost's key for an Instagram boost. When a key is refused, `extensions.status` tells you why. `createPost` returns 409 while the first attempt is still processing, so wait and retry with the same key, and 400 when the payload differs from the first attempt. The boost mutations return 409 in both cases, and also when the key was used for a boost on the other platform. Other writes, such as `addComment`, have no idempotency key. If their response is lost, check the social network or API state before deciding whether another attempt is safe. Two different limits both report 429, and they need different handling. The dispatch budget only rejects root fields once five API calls have already started in the same request, and its message begins `Query exceeds the per-request dispatch budget`: split those fields into requests of at most five, with no need to wait. Any other 429 is a rate limit or quota: wait `extensions.retryAfter` seconds when it is present, otherwise back off before retrying, and retry only the fields that failed. For a 5xx on a **read**, retrying is safe, with one exception: `generatePost` is a query, but a retry calls the AI generator again, counts as another API call, and returns different text. For a 5xx on a write without idempotency, reconcile whether it took effect before retrying. ## Common statuses | `extensions.status` | Meaning | What to do | | - | - | - | | 400 | Invalid input our API rejected | Fix the request; retrying will not help | | 401 | Missing or invalid API key | Check the `Authorization` header | | 402 | Plan does not include this feature | Check your plan | | 403 | Key valid but not permitted for this action | Check profile keys and plan | | 404 | The thing you asked for does not exist | Verify the id | | 409 | The idempotency key is in use: the first request is still running, or a boost key was used with different arguments | If the first request is still running, wait and retry unchanged; otherwise use a new key for the new operation | | 429 | Rate limit, quota, or dispatch budget | If the message names the dispatch budget, split the request. Otherwise wait `retryAfter`, or back off if it is absent | | 500 | Something went wrong on our side | Retry a read (not `generatePost`, which bills again); reuse the same idempotency key where supported, otherwise verify a write before retrying | Errors raised by GraphQL itself before execution, such as an unknown field, a wrong argument type, or an invalid enum value, have no `extensions.status`. They return HTTP 400 or 200 depending on your `Accept` header, as described above. They mean the query itself is wrong, and no API call was made or billed. # GraphQL Limits and Billing Source: https://www.ayrshare.com/docs/apis/graphql/limits How Ayrshare counts GraphQL requests against your API quota, plus the query depth, field, alias, directive, and request size limits. ## How requests are counted **Each executed root operation field is one API call.** A GraphQL request that asks for four root fields is billed as four API calls, exactly as if you had made four REST requests. Aliases at the root count separately because each one dispatches an operation. Nested fields selected from a typed response do not add API calls. ```graphql theme={"system"} { history: postHistory(lastDays: 7) accounts: user comments: comments(id: "abc123") analytics: socialAnalytics(platforms: [INSTAGRAM]) } ``` One HTTP request, **four API calls** against your plan's quota and rate limit. This is intentional and it is the honest model: each root field does the same work on our side as the equivalent REST call, so it costs the same. GraphQL saves you round trips and lets you fetch exactly what you need in one place; it is not a way to get more API calls than your plan includes. The practical consequences:
    • Your existing plan limits apply unchanged. See [API limits](/docs/apis/overview) for your plan's allowance.
    • Asking for a root operation you do not need costs the same as calling it over REST. Selecting the nested response fields you need does not add calls.
    • A request can be rate limited *partway through*. Root fields that completed keep their data, and only the throttled root fields return errors — see [Partial success](/docs/apis/graphql/errors#partial-success).
    ## Query limits The endpoint enforces a few limits on the shape and size of a query. They exist so that a single request cannot consume an unreasonable amount of our capacity, and they are set well above what ordinary use requires — if you are hitting one, it is usually a sign of an accidentally generated query rather than a real need. | Limit | Value | How you find out | | - | - | - | | API calls per request | 5 | `extensions.status` 429 on each extra root field | | Query nesting depth | 15 | Query rejected before it runs | | Fields per request | 300 | Query rejected before it runs | | Aliases per request | 25 | Query rejected before it runs | | Directives per request | 50 | Query rejected before it runs | | Request body size | 64 KB | HTTP 413 | A rejected query returns HTTP 400 if you send `Accept: application/graphql-response+json`, and HTTP 200 otherwise, with an `errors` array and no `data` in both cases. See [Errors](/docs/apis/graphql/errors). ### API calls per request The important one. A single request can trigger at most **5** API calls, so the four-root-field example above is fine but a twenty-root-field one is not. Once five dispatches have started, additional root fields return a 429 explaining the limit; already-dispatched fields can still return their data. If you need more, split the query across requests. If you find yourself doing that routinely for a legitimate workload, [get in touch](/docs/help-center/overview) — the limit is deliberately conservative for launch and raising it is straightforward, whereas lowering it later would break integrations, so it starts small. ### Depth, fields, aliases and directives These are checked before anything runs, so exceeding one costs you no API calls at all. They are also comfortably clear of a full introspection query, which is around 180 fields and 12 levels deep with no aliases or directives, so a client that fetches the entire schema on startup works normally. ### Request body size 64 KB, which is generous for a query and far too small for a file. This is why media cannot be uploaded through GraphQL — see [Uploading media](/docs/apis/graphql/using-the-api#uploading-media) for the supported path. ## Timeouts A request is given up to 120 seconds overall. Root fields in a **query may run concurrently**, so its duration is generally driven by its slowest dependency. Root fields in a **mutation run serially**, so several slow mutations can accumulate toward the timeout. Either form can still place up to five concurrent or sequential dispatches' worth of load on the backend; split known-slow work when appropriate. ## What is not limited
    • **Reading the schema.** Introspection is unauthenticated and unmetered. Browsing what exists is free; running an operation is not.
    • **Malformed queries.** A query GraphQL rejects — unknown field, wrong type, invalid enum value — never reaches our API and is never billed.
    These limits are reviewed against real usage. If one of them is the wrong shape for a legitimate integration, tell us — that feedback is more useful than a workaround. # GraphQL API Overview Source: https://www.ayrshare.com/docs/apis/graphql/overview Query supported Ayrshare social media operations through a single GraphQL endpoint. The GraphQL API exposes a supported subset of Ayrshare's REST capabilities through a single endpoint. Each exposed field calls the same underlying controller as its REST counterpart, so authentication, entitlements, quotas, and response data follow the REST behavior. REST remains the broader API; use the schema or Explorer to see exactly which operations GraphQL supports. What it adds is the ability to ask for several things in one request and to discover the currently supported GraphQL surface from the schema itself, without reading documentation page by page. The schema describes every supported request field and the typed selections available on structured responses; operations that return the REST envelope as `JSON` preserve that complete payload. ``` https://api.ayrshare.com/graphql ``` Send a `POST` with a JSON body containing a `query`, exactly as with any GraphQL endpoint. `GET` and `DELETE` return `405 Method Not Allowed` — queries over `GET` are optional in the GraphQL specification and are not supported here. ## Your first query ```bash theme={"system"} curl -X POST https://api.ayrshare.com/graphql \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"query": "{ postHistory(lastDays: 7) }"}' ``` The response is the same JSON envelope the [REST history endpoint](/docs/apis/history/get-history) returns, wrapped in GraphQL's `data` field: ```json theme={"system"} { "data": { "postHistory": { "history": [ { "id": "RkQ8uXg2jT7bNd1Wq0Ya", "status": "success", "post": "Hello world" } ], "refId": "9d2a7c41f0b8e6d35a1c", "count": 1, "lastUpdated": "2026-09-24T12:00:00.000Z", "nextUpdate": "2026-09-24T12:00:00.000Z" } } } ``` ## Asking for several things at once The reason to reach for GraphQL is a request like this, which would be four REST calls: ```graphql theme={"system"} { history: postHistory(lastDays: 7) accounts: user comments: comments(id: "abc123") analytics: socialAnalytics(platforms: [INSTAGRAM]) } ``` One round trip returns all four. Note that these are **four root operation fields and four API calls for billing purposes**, not one. Selecting nested response fields does not add calls — see [Limits and Billing](/docs/apis/graphql/limits). ## Authentication Identical to REST. Send your API key as a bearer token: ``` Authorization: Bearer YOUR_API_KEY ``` If your account uses User Profiles, operations that expose `profileKey` can select a profile in either of two ways:
    • Send `Profile-Key` as the request-wide default.
    • Pass `profileKey` on an individual field to override that default, allowing one request to act on more than one profile.
    The field argument wins when both are present. Account-level or primary-account-only fields do not expose `profileKey` and may reject a `Profile-Key` header; for example, `createProfile` must use the primary API key without that header. Check each field's schema definition for its scope, and see [Managing multiple users](/docs/multiple-users/business-plan-overview) for how profile keys work. ## Try it without writing code The [GraphQL Explorer](https://app.ayrshare.com/graphql-explorer) is an interactive browser for the current schema. It lists every available GraphQL operation with its arguments and descriptions, autocompletes as you type, and runs queries against your account. You do not need an API key to browse the schema — the schema is public, the same way this documentation is. You do need one to run a query, because every operation goes through the same authentication as REST. ## Should you use GraphQL or REST? REST remains the primary interface and the one most of our documentation, [SDKs](/docs/packages-guides/overview), and integrations are built around. Reach for GraphQL when:
    • You need several unrelated pieces of data and want them in one round trip.
    • You want machine-readable operation names, arguments, input objects, enums, and typed response selections. Most responses remain `JSON` so they preserve the complete REST envelope; `createPost` currently returns a typed `PostResult`.
    • You are exploring the API and want to see what exists without moving between documentation pages.
    Stay with REST when:
    • You are uploading files. Media bytes cannot travel through a GraphQL request — see [Uploading media](/docs/apis/graphql/using-the-api#uploading-media) for the supported path.
    • You are using one of our [SDKs or no-code integrations](/docs/packages-guides/overview), which speak REST.
    • You want the smallest possible dependency footprint. A REST call needs nothing but an HTTP client.
    Both interfaces are supported side by side, and you can mix them freely in the same integration. ## Read next
    • [Using the API](/docs/apis/graphql/using-the-api) — finding operations, argument types, and uploading media.
    • [Errors](/docs/apis/graphql/errors) — which failures change the HTTP status, why failed operations still return HTTP 200, and how to handle partial success.
    • [Limits and Billing](/docs/apis/graphql/limits) — query size caps and how requests are counted.
    # Using the GraphQL API Source: https://www.ayrshare.com/docs/apis/graphql/using-the-api Find operations, understand argument types and enums, create posts, and upload media through the Ayrshare GraphQL API. ## Finding an operation The schema is authoritative for the supported GraphQL subset: operation roots, names, arguments, input types, and enum values. Descriptions provide usage guidance, and the [GraphQL Explorer](https://app.ayrshare.com/graphql-explorer) exposes both. For REST-only capabilities, use the [REST API reference](/docs/apis/overview). Operation names follow the REST endpoints they reach, in camelCase. `GET /history` is `postHistory`, `GET /analytics/social` is `socialAnalytics`, `POST /post` is `createPost`. ## Queries read, mutations write Queries are reads and validations that change nothing on our side. Mutations publish or change state, start work, or send email. Neither the REST HTTP verb nor the cost of a call decides the root:
    • `validatePost` checks a post without publishing it.
    • `validateMedia` checks that a media URL is reachable.
    • `generatePost` is a query because nothing on our side changes. It still counts as an API call and returns different text each time, so make sure a client cache or automatic refetch does not repeat it without you noticing.
    • `mediaUploadUrl` is a mutation because it creates an upload URL for your account.
    • `linkAnalytics` is a mutation because it can request an emailed report.
    • `userBatch` is a mutation because it starts an export job.
    Use the Explorer or schema to confirm the root for each supported operation. ## Creating a post `createPost` takes a single `input` argument, so the whole post is one object: ```graphql theme={"system"} mutation { createPost(input: { post: "Hello from GraphQL" platforms: [LINKEDIN] idempotencyKey: "post-2026-09-01-001" }) { status id } } ``` The input mirrors the documented [REST post endpoint](/docs/apis/post/post), including per-network option objects and the REST forms that accept more than one JSON shape: ```graphql theme={"system"} mutation { createPost(input: { post: "New video" platforms: [YOUTUBE] mediaUrls: ["https://example.com/video.mp4"] idempotencyKey: "youtube-video-001" youTubeOptions: { title: "My video" visibility: PUBLIC } }) { status id postIds { platform postUrl } } } ``` A post can succeed on one network and fail on another. That is not a request failure — see [Errors](/docs/apis/graphql/errors#partial-success-on-multi-network-posts). ## Argument types Most arguments are ordinary strings, numbers and booleans. Three cases are worth knowing about. ### Enums Many string arguments with a closed supported set are GraphQL enums, written **unquoted and upper case**: ```graphql theme={"system"} { socialAnalytics(platforms: [INSTAGRAM, TIKTOK]) } ``` Not `"instagram"`. The server maps each accepted enum to the exact value expected by the underlying REST controller — often, but not always, a lowercase string. An invalid enum is rejected during GraphQL validation before the operation runs, so a typo costs you nothing. Some arguments share a name across operations but accept different values, because the endpoints genuinely differ. `reviews(platform:)` accepts only `GMB` and `FACEBOOK`, since those are the only networks with reviews. Your client's autocomplete will show the correct set for each operation. ### The JSON scalar A few arguments are typed as `JSON` rather than a specific type. This happens where a value legitimately has more than one shape and no single GraphQL type could describe it honestly:
    • `explainError(code:)` accepts `215` or `"215"`, because customers hold error codes as both.
    • `createPost(input:)` uses JSON for fields such as `post` and `mediaUrls`, which can be shared values or per-platform objects.
    • `createAutomation(triggers:, actions:)` take arrays whose fields depend on each entry's `type`.
    • `boostFacebookPost(interests:)` accepts Meta interest ids as strings or numbers.
    Pass the same value you would send in the corresponding REST body. A `JSON` argument is not a loophole: the resolver validates it before dispatch, so an invalid value returns a validation error and consumes no API call. ### Optional arguments and nulls For optional arguments and optional fields inside input objects, explicit `null` is normalized to omission. Null members inside lists are preserved when the list type permits them. ## Reading responses Most operations return a `JSON` scalar containing the complete REST response envelope, so select the root field without subfields. `createPost` currently returns a typed `PostResult`, so select its fields: ```graphql theme={"system"} mutation { createPost(input: { post: "Hi" platforms: [LINKEDIN] idempotencyKey: "linkedin-hi-001" }) { status id postIds { platform postUrl } errors { platform message code } } } ``` Typed response types such as `PostResult` also include `raw: JSON!`, containing the complete unmodified REST response: ```graphql theme={"system"} mutation { createPost(input: { post: "Hi" platforms: [LINKEDIN] idempotencyKey: "linkedin-hi-001" }) { status raw } } ``` `raw` is permanent and is there so a new field appearing in the REST response is never unreachable from GraphQL while we catch up on typing it. If you need something the typed fields do not expose, ask for `raw`. ## Uploading media **Media bytes cannot travel through a GraphQL request.** A GraphQL request is a single JSON document under a 64 KB limit, so there is no field that accepts a file, and encoding an image as base64 inside the query would exceed that limit for anything larger than a thumbnail. The supported path avoids the problem entirely, and is faster than uploading through an API in any case, because your bytes go straight to storage: 1. Ask for an upload URL: ```graphql theme={"system"} mutation { mediaUploadUrl(contentType: "image/jpeg") } ``` 2. Read `data.mediaUploadUrl.uploadUrl`, `accessUrl`, and `contentType` from the returned JSON. 3. `PUT` your file **directly to `uploadUrl`**, not to the Ayrshare API, setting the request's `Content-Type` header to the returned `contentType`. 4. After the upload succeeds, pass `accessUrl` in `createPost.input.mediaUrls`: ```graphql theme={"system"} mutation { createPost(input: { post: "With a photo" platforms: [INSTAGRAM] mediaUrls: ["https://the-returned-access-url"] idempotencyKey: "instagram-photo-001" }) { status postIds { platform postUrl } } } ``` Treat `uploadUrl` as a short-lived write credential and do not log or expose it. `accessUrl` is the media URL used when creating the post. You can also keep using the [REST upload endpoints](/docs/apis/media/upload-media) and reference the resulting URLs from GraphQL. The two interfaces share the same media library. ## Read next
    • [Errors](/docs/apis/graphql/errors) — HTTP status codes, error shapes, and partial success.
    • [Limits and Billing](/docs/apis/graphql/limits) — query size caps and how requests are counted.
    # Auto Hashtags Source: https://www.ayrshare.com/docs/apis/hashtags/auto-hashtags POST /hashtags/auto Add relevant AI-generated hashtags to your post text, embedded within the text or placed at the end, with a max hashtag count and post language code. When creating your post, add relevant hashtags by either embedding them within the text or placing them at the end. Our advanced AI system will analyze your content to generate appropriate hashtags based on the subject matter. Although you can use any language for your post, writing in English typically yields the best results for our hashtag generation process. ## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` **Profile-Key** `string` [Profile Key](/docs/apis/overview#profile-key-format) of a User Profile. Format: `Profile-Key: PROFILE_KEY` ## Body Parameters **post** `string` *required* Post text to add the hashtags. Max length 1,000 characters. **position** `string` validate **max** `integer` (default: 2) Integer of max number of Hashtags to add. Max value range 1 to 10. Note: Instagram allows a maximum of 5 hashtags per post. **language** `string` Specify the [language code](/docs/iso-codes/language) of the language of the post to assist the hashtag algorithm. ```json theme={"system"} { "language": "fr" } ``` ```bash cURL theme={"system"} curl \ -H "Authorization: Bearer API_Key" \ -d '{"post": "Today is a great day!", "max": 3, "position": "auto"}' -X POST https://api.ayrshare.com/api/hashtags/auto ``` ```javascript JavaScript theme={"system"} const API_KEY = "API_KEY"; fetch(`https://api.ayrshare.com/api/hashtags/auto`, { method: "POST", headers: { "Authorization": `Bearer ${API_KEY}` }, body: JSON.stringify({ post: "Today is a great day!", // required max: 3, // optional, range 1-5 position: "auto" // optional, "auto" or "end" }) }) .then((res) => res.json()) .then((json) => console.log(json)) .catch(console.error); ``` ```python Python theme={"system"} import requests payload = { 'post': 'Today is a great day!', 'max': 3, # optional, range 1-5 'position': 'auto' # optional, 'auto' or 'end' } headers = {'Content-Type': 'application/json', 'Authorization': 'Bearer API_KEY'} r = requests.post('https://api.ayrshare.com/api/hashtags/auto', json=payload, headers=headers) print(r.json()) ``` ```php PHP theme={"system"} request( 'POST', 'https://api.ayrshare.com/api/hashtags/auto', [ 'headers' => [ 'Content-Type' => 'application/json', 'Authorization' => 'Bearer API_KEY' ], 'json' => [ 'post' => 'Today is a great day!', 'max' => 3, 'position' => 'auto' ] ] ); echo json_encode(json_decode($res->getBody()), JSON_PRETTY_PRINT); ``` ```javascript 200 Hashtagged Post Returned theme={"system"} { post: "Disney’s trouble with Oswald the #LuckyRabbit is a great lesson #ForStartups in a crisis" } ``` # Check Banned Hashtags Source: https://www.ayrshare.com/docs/apis/hashtags/check-hashtags GET /hashtags/banned Check whether Instagram or other social networks have banned a hashtag. Pass the hashtag with or without the # sign and get back a true or false result. A banned hashtag checker to validate if the given hashtag has been banned by Instagram or other social networks. ## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` ## Query Parameters **hashtag** `string` *required* The hashtag to validate. Format: "hashtag" or "#hashtag" ```bash cURL theme={"system"} curl --location --request GET 'https://api.ayrshare.com/api/hashtags/banned?hashtag=%23bikinibody' \ --header 'Authorization: Bearer API_KEY' ``` ```javascript JavaScript theme={"system"} const myHeaders = new Headers(); myHeaders.append("Authorization", "Bearer API_KEY"); const urlencoded = new URLSearchParams(); const requestOptions = { method: 'GET', headers: myHeaders, }; fetch("https://api.ayrshare.com/api/hashtags/banned?hashtag=%23bikinibody", requestOptions) .then(response => response.json()) .then(result => console.log(result)) .catch(error => console.log('error', error)); ``` ```python Python theme={"system"} import requests # Your API key API_KEY = "Bearer API_KEY" # Headers headers = { "Authorization": API_KEY } # URL url = "https://api.ayrshare.com/api/hashtags/banned?hashtag=%23bikinibody" # Making the GET request response = requests.get(url, headers=headers) # Checking if the request was successful if response.status_code == 200: # Parse JSON response result = response.json() print(result) else: print("Error:", response.status_code) ``` ```php PHP theme={"system"} ``` ```csharp C# theme={"system"} using System; using System.Net.Http; using System.Threading.Tasks; namespace ConsoleApp { class Program { static async Task Main(string[] args) { // Your API key string apiKey = "Bearer API_KEY"; // URL string url = "https://api.ayrshare.com/api/hashtags/banned?hashtag=%23bikinibody"; using (var httpClient = new HttpClient()) { // Set the Authorization header with your API key httpClient.DefaultRequestHeaders.Add("Authorization", apiKey); try { // Make the GET request HttpResponseMessage response = await httpClient.GetAsync(url); if (response.IsSuccessStatusCode) { // Read the response content as a string string result = await response.Content.ReadAsStringAsync(); Console.WriteLine(result); } else { Console.WriteLine($"Error: {response.StatusCode}"); } } catch (HttpRequestException e) { Console.WriteLine($"Request exception: {e.Message}"); } } } } } ``` ```json 200 theme={"system"} { "hashtag": "#bikinibody", "banned": true } ``` ```json 400 theme={"system"} { "action": "request", "status": "error", "code": 101, "message": "Missing or incorrect parameters. Please verify with the docs. .../ayrshare.com/rest-api/endpoints" } ``` # Hashtags API Overview Source: https://www.ayrshare.com/docs/apis/hashtags/overview Hashtags API: add AI-generated hashtags to post text, get TikTok hashtag suggestions, check for banned hashtags, and search Instagram posts by hashtag. ## Hashtags API Features The Hashtags API provides powerful tools to enhance your social media content with relevant hashtags:
    • **Intelligent Hashtag Generation:** Automatically create optimized hashtags based on your content's keywords, leveraging real-time popularity data to maximize visibility and engagement.
    • **Cross-Platform Support:** Generate platform-specific hashtags tailored for Facebook, Instagram, LinkedIn, Threads, TikTok, Twitter, and YouTube to optimize performance on each network.
    • **Instagram Hashtag Research:** Search and analyze the most popular or relevant Instagram posts for any given hashtag to inform your content strategy and identify trending topics.
    • **Performance Optimization:** Improve post discoverability and reach by incorporating trending, relevant hashtags that align with your content and target audience.
    # Recommend Hashtags Source: https://www.ayrshare.com/docs/apis/hashtags/recommend-hashtags GET /hashtags/recommend Get recommended hashtags from TikTok based on a single keyword, with data and view counts for each suggestion. Spaces in the keyword will be removed. Get suggestions for hashtags based on a keyword. Data and view counts are generated from TikTok. ## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` ## Query Parameters **keyword** `string` *required* A single keyword to get recommended hashtags from TikTok. Spaces in the keyword will be removed. ```bash cURL theme={"system"} curl \ -H "Authorization: Bearer API_KEY" \ -X GET https://api.ayrshare.com/api/hashtags/recommend?keyword=apple ``` ```javascript JavaScript theme={"system"} const API_KEY = "API_KEY"; fetch("https://api.ayrshare.com/api/hashtags/recommend?keyword=apple", { method: "GET", headers: { "Authorization": `Bearer ${API_KEY}` } }) .then((res) => res.json()) .then((json) => console.log(json)) .catch(console.error); ``` ```python Python theme={"system"} import requests headers = {'Authorization': 'Bearer API_KEY'} r = requests.get('https://api.ayrshare.com/api/hashtags/recommend?keyword=apple', headers=headers) print(r.json()) ``` ```php PHP theme={"system"} request( 'GET', 'https://api.ayrshare.com/api/hashtags/recommend?keyword=apple', [ 'headers' => [ 'Content-Type' => 'application/json', 'Authorization' => 'Bearer API_KEY' ] ] ); echo json_encode(json_decode($res->getBody()), JSON_PRETTY_PRINT); ``` ```csharp C# theme={"system"} using System; using System.Net.Http; using System.Threading.Tasks; namespace HashtagsRecommendGETRequest_csharp { class HashtagsRecommend { static async Task Main(string[] args) { string API_KEY = "API_KEY"; string url = "https://api.ayrshare.com/api/hashtags/recommend?keyword=apple"; using (var client = new HttpClient()) { client.DefaultRequestHeaders.Add("Authorization", "Bearer " + API_KEY); try { var response = await client.GetStringAsync(url); Console.WriteLine(response); } catch (HttpRequestException ex) { Console.WriteLine($"Error: {ex.Message}"); } } } } } ``` ```json 200: Success theme={"system"} { "keyword": "apple", "recommendations": [ { "viewCount": 71950998550, // The number of views that the recommended hashtag has received. "name": "apple" // Suggested hashtag }, { "viewCount": 6318280101, "name": "applewatch" }, { "viewCount": 2486358394, "name": "applepencil" }, { "viewCount": 2142842131, "name": "applemusic" }, { "viewCount": 1506224079, "name": "applesquad" }, { "viewCount": 1307287255, "name": "apples" }, { "viewCount": 1008293028, "name": "applepie" }, { "viewCount": 788388838, "name": "applejuice" }, { "viewCount": 1024901638, "name": "appletv" }, { "viewCount": 679033194, "name": "applechallenge" } ] } ``` ```json 400: Bad Request Missing keyword theme={"system"} { "action": "request", "status": "error", "code": 101, "message": "Missing or incorrect parameters. Please verify with the docs. .../ayrshare.com/rest-api/endpoints" } ``` # Search Hashtags Source: https://www.ayrshare.com/docs/apis/hashtags/search-hashtags GET /hashtags/search Find public Instagram Media that has been tagged with specific hashtags Search based on a hashtag keyword to get a list of Instagram media posts that have been tagged with that hashtag.
    • You can query a maximum of 30 unique hashtags on behalf of an Instagram Business or Creator Account within a rolling, 7-day period. Once you query a hashtag, it will count against this limit for 7 days. Subsequent queries on the same hashtag within this time frame will not count against your limit, and will not reset its initial query 7-day timer.
    • You cannot comment on hashtagged media objects discovered through the API.
    • Hashtags on Stories are not supported.
    • Emojis in hashtag queries are not supported.
    • The API will return an error for any requests that include hashtags that we have deemed sensitive or offensive.
    ## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` ## Query Parameters **keyword** `string` *required* The keyword to search for matching hashtags. ```bash Search for the hashtag "wisdom" theme={"system"} https://api.ayrshare.com/api/hashtags/search?keyword=wisdom ``` **searchType** `string` (default: top) The type of search to perform. Valid values: `top`, `recent`. Top will return the most popular hashtags. Recent will return the most recent hashtags. Default: `top`. ```bash cURL theme={"system"} curl \ -H "Authorization: Bearer API_KEY" \ -X GET https://api.ayrshare.com/api/hashtags/search?keyword=wisdom&searchType=top ``` ```javascript JavaScript theme={"system"} const API_KEY = "API_KEY"; fetch("https://api.ayrshare.com/api/hashtags/search?keyword=wisdom&searchType=top", { method: "GET", headers: { "Authorization": `Bearer ${API_KEY}` } }) .then((res) => res.json()) .then((json) => console.log(json)) .catch(console.error); ``` ```python Python theme={"system"} import requests headers = {'Authorization': 'Bearer API_KEY'} r = requests.get('https://api.ayrshare.com/api/hashtags/search?keyword=wisdom&searchType=top', headers=headers) print(r.json()) ``` ```php PHP theme={"system"} request( 'GET', 'https://api.ayrshare.com/api/hashtags/search?keyword=wisdom&searchType=top', [ 'headers' => [ 'Content-Type' => 'application/json', 'Authorization' => 'Bearer API_KEY' ] ] ); echo json_encode(json_decode($res->getBody()), JSON_PRETTY_PRINT); ``` ```csharp C# theme={"system"} using System; using System.Net.Http; using System.Threading.Tasks; namespace HashtagsSearchGETRequest_csharp { class HashtagsSearch { static async Task Main(string[] args) { string API_KEY = "API_KEY"; string url = "https://api.ayrshare.com/api/hashtags/search?keyword=wisdom&searchType=top"; using (var client = new HttpClient()) { client.DefaultRequestHeaders.Add("Authorization", "Bearer " + API_KEY); try { var response = await client.GetStringAsync(url); Console.WriteLine(response); } catch (HttpRequestException ex) { Console.WriteLine($"Error: {ex.Message}"); } } } } } ``` ```json 200: Success theme={"system"} { "status": "success", "hashtag": { "id": "17843798701036069", "name": "wisdom" }, "searchResults": [ { "caption": "🙂96% Percent of People have no clue this method exists ... For manifesting money , there is powerful a manifestation technique it changes your beliefs on reality in the deepest level of the subconscious mind it literally changed my life give it an honest try . Click the link in our bio to see if this works for you too . (worked for me )", "children": { "data": [ { "id": "18364392676136006" }, { "id": "18008030660493382" }, { "id": "17866325790184001" } ] }, "commentsCount": 174, "id": "17861532840277137", "likeCount": 1389, "mediaType": "CAROUSEL_ALBUM", "mediaUrl": "https://scontent-sjc3-1.cdninstagram.com/v/t51.29350-15/466382723_420103424530861_7360358065352125344_n.jpg?_nc_cat=109&ccb=1-7&_nc_sid=18de74&_nc_ohc=c-_PWScwGQAQ7kNvgFPrTeK&_nc_zt=23&_nc_ht=scontent-sjc3-1.cdninstagram.com&edm=APCawUEEAAAA&_nc_gid=AT1Ax3hZh5lt3kJskGj3Hj6&oh=00_AYBDkqi4pSdedYxDfUnAm9c93AlyXabWzJM81UySCuIpDg&oe=67406A28", "permalink": "https://www.instagram.com/p/DCdDF6_Sa31/", "timestamp": "2024-11-17T01:22:29+0000" }, { "caption": "🙂96% Percent of People have no clue this method exists ... For manifesting money , there is powerful a manifestation technique it changes your beliefs on reality in the deepest level of the subconscious mind it literally changed my life give it an honest try . Click the link in our bio to see if this works for you too . (worked for me )", "children": { "data": [ { "id": "17854668297321577" }, { "id": "17953717721743238" } ] }, "commentsCount": 40, "id": "18054468655821869", "likeCount": 371, "mediaType": "CAROUSEL_ALBUM", "mediaUrl": "https://scontent-sjc3-1.cdninstagram.com/v/t51.29350-15/467316183_1100515004943506_6021956524402070992_n.jpg?_nc_cat=101&ccb=1-7&_nc_sid=18de74&_nc_ohc=LQVF-7HyOE4Q7kNvgEPZ98d&_nc_zt=23&_nc_ht=scontent-sjc3-1.cdninstagram.com&edm=APCawUEEAAAA&_nc_gid=AT1Ax3hZh5lt3kJskGj3Hj6&oh=00_AYArRIS7xth-eI3rRWlvLhMklRJLBIH5rZ66akHfuSnxjg&oe=6740726A", "permalink": "https://www.instagram.com/p/DCdDZb5SzUj/", "timestamp": "2024-11-17T01:25:09+0000" }, { "caption": "Setting intentions rather than expectations is a valuable way to keep you from overthinking, help you stay closer to reality, and move with mental clarity. \n\nYou can develop this kind of mindful skill by joining my 8-week "Slow School" live online course. It starts Dec 3rd. Check the link in my bio to learn more 🖤", "children": { "data": [ { "id": "18471207271027384" }, { "id": "17875576494200786" } ] }, "commentsCount": 33, "id": "18057421303730368", "likeCount": 5601, "mediaType": "CAROUSEL_ALBUM", "mediaUrl": "https://scontent-sjc3-1.cdninstagram.com/v/t51.2885-15/467022186_992465279306045_4614070861842639887_n.jpg?_nc_cat=105&ccb=1-7&_nc_sid=18de74&_nc_ohc=pchds80KU9oQ7kNvgEaCWkZ&_nc_zt=23&_nc_ht=scontent-sjc3-1.cdninstagram.com&edm=APCawUEEAAAA&oh=00_AYDqG9VA4vJzBvXw6nzFTXc7rU0xVnsTJBOpptBbCEI9Gw&oe=67408C3D", "permalink": "https://www.instagram.com/p/DCZT369M_07/", "timestamp": "2024-11-15T14:32:12+0000" }, "count": 25, "lastUpdated": "2024-11-19T20:09:02.830Z", "nextUpdate": "2024-11-19T20:20:02.830Z" ] } ``` ```json 400: Bad Request theme={"system"} { "action": "hashtag search", "status": "error", "code": 380, "message": "Error searching Instagram Hashtags. Could not find a matching hashtag" } ``` # Posts History Source: https://www.ayrshare.com/docs/apis/history/get-history GET /history Retrieve posts sent through Ayrshare from newest to oldest, filtered by date range, status, social network, post type, auto repost ID, or the last n days. > [!NOTE] > > **Targeting X/Twitter?** Starting March 31, 2026, all X operations require your own API credentials. After linking X via OAuth, include these 2 headers in your request: > > `X-Twitter-OAuth1-Api-Key` — Your API Key (Consumer Key) > > `X-Twitter-OAuth1-Api-Secret` — Your API Key Secret (Consumer Secret) > > **One-time setup per Ayrshare account.** You create one X Developer App and reuse the same API Key and Secret across every sub-profile / end-user you link. You do *not* create a new app per customer. > > Not linked yet? See the [full setup guide](/docs/dashboard/connect-social-accounts/x-twitter-byo-keys) to connect your X account. > > Your keys are never logged or stored by Ayrshare. Get a history of posts sent via Ayrshare, in descending order (most recent to oldest). With this endpoint you can retrieve posts from a specific date range, status, social network, or the last n days. History `status` fields values:
    • `awaiting approval`: Posts are awaiting to be approved via the [approval workflow](/docs/apis/post/overview#approval-workflow).
    • `deleted`: Post has been deleted. Note: deleted posts are only returned with the status query filter. Please see below.
    • `error`: An error occurred with one or more social networks.
    • `paused`: A scheduled post that has been paused.
    • `pending`: The post has not yet been processed. Typically a scheduled post.
    • `success`: The post was successfully sent to all social networks.
    Additional information:
    • The [Ayrshare Post ID](/docs/apis/overview#ayrshare-post-id) is returned in the response `id` field.
    • To retrieve posts that originated outside of Ayrshare, such as posts manually created directly at the social network, use the [history platform](/docs/apis/history/history-platform) endpoint.
    • If only an individual post is required, use the [post history by id](/docs/apis/history/get-history-id) endpoint.
    • The history endpoint JSON results are cached for 1 minute if the `limit` is greater than the default value of 25.
    ## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` **Profile-Key** `string` [Profile Key](/docs/apis/overview#profile-key-format) of a User Profile. Format: `Profile-Key: PROFILE_KEY` ## Query Parameters **limit** `number` (default: 25) Returns the last *n* number of posts. For example, if you only want the most recent post, set `limit=1`. Default: `25` posts. Max value: `1000`. **platforms** `array` Filter by social network platforms. Platform values: `bluesky`, `facebook`, `gmb`, `instagram`, `linkedin`, `pinterest`, `reddit`, `snapchat`, `telegram`, `threads`, `tiktok`, `twitter`, `youtube`. Note, uses `OR` logic: `["facebook", "instagram"]` returns posts from either. **startDate** `string` Return posts from and including this start date. Start date for the history in ISO 8601 format. For example, use format `YYYY-MM-DDThh:mm:ssZ` and send as `2026-07-08T12:30:00Z`. Please see [utctime](https://www.utctime.net/) for more examples. **endDate** `string` Return posts up to and including this end date. End date for the history in ISO 8601 format. For example, use format `YYYY-MM-DDThh:mm:ssZ` and send as `2026-07-08T12:30:00Z`. Please see [utctime](https://www.utctime.net/) for more examples. **lastDays** `number` (default: 30) Returns the last n days of posts by the publish date, i.e. `scheduleDate`. Default 30 days.
    • The `lastDays` will be ignored if `startDate` and `endDate` are provided.
    • If the value is zero 0 will return the entire history of posts.
    • If the value is greater than 0, it will return the last n days of posts.
    • For example, `lastDays=5` returns the last 5 days of posts and `lastDays=0` returns all posts determined by the `limit`.
    **status** `string` Filter by current status of post. Valid values: `success`, `error`, `processing`, `pending`, `paused`, `deleted`, and `awaiting approval`. Processing indicates the post is currently being sent. Pending indicates the post is scheduled to be posted at a future date. > [!NOTE] > > Deleted posts are not returned by default. They are only returned with the status query filter set to `status=deleted`. **type** `string` The type of post to retrieve, either post that were sent immediately or scheduled post using the `scheduleDate` field. Values: `immediate` or `scheduled`. **autoRepostId** `string` When creating an auto repost an ID is assigned to track that series of posts. Retrieve posts by auto repost ID or use the value of `all` to get all auto reposts. ```bash cURL theme={"system"} curl \ -H "Authorization: Bearer API_KEY" \ -X GET https://api.ayrshare.com/api/history?startDate=2025-01-01T12:30:00Z&endDate=2025-03-01T12:30:00&limit=100 ``` ```javascript JavaScript theme={"system"} const API_KEY = "API_KEY"; fetch("https://api.ayrshare.com/api/history?startDate=2025-01-01T12:30:00Z&endDate=2025-03-01T12:30:00&limit=100", { method: "GET", headers: { "Authorization": `Bearer ${API_KEY}` } }) .then((res) => res.json()) .then((json) => console.log(json)) .catch(console.error); ``` ```python Python theme={"system"} import requests headers = {'Authorization': 'Bearer API_KEY'} r = requests.get('https://api.ayrshare.com/api/history?startDate=2025-01-01T12:30:00Z&endDate=2025-03-01T12:30:00&limit=100', headers=headers) print(r.json()) ``` ```php PHP theme={"system"} true, CURLOPT_HTTPHEADER => $headers ]); $response = curl_exec($curl); if ($response === false) { echo 'Curl error: ' . curl_error($curl); } else { echo json_encode(json_decode($response), JSON_PRETTY_PRINT); } curl_close($curl); ``` ```csharp C# theme={"system"} using System; using System.Net.Http; using System.Threading.Tasks; namespace HistoryGETRequest_csharp { class History { static async Task Main(string[] args) { string API_KEY = "API_KEY"; string url = "https://api.ayrshare.com/api/history?startDate=2025-01-01T12:30:00Z&endDate=2025-03-01T12:30:00&limit=100"; using (var client = new HttpClient()) { client.DefaultRequestHeaders.Add("Authorization", "Bearer " + API_KEY); try { var response = await client.GetStringAsync(url); Console.WriteLine(response); } catch (HttpRequestException ex) { Console.WriteLine($"Error: {ex.Message}"); } } } } } ``` ```json 200: Success theme={"system"} { "history": [ { "errors": [], "post": "This is the post I sent", "platforms": [ "twitter", "facebook" ], "postIds": [ { "status": "success", "id": "1288968500063775749", // Twitter Social Post ID "platform": "twitter" }, { "id": "104923907983682_108683297607743", // Facebook Social Post ID "status": "success", "platform": "facebook" } ], "urls": [], "type": "now", "notes": "Approved by John Smith", // Reference notes set via /post "created": "2022-05-20T17:25:06Z", "status": "deleted", "scheduleDate": { // In a future release changed to "scheduleDate": "2020-11-05T12:21:29Z" "_seconds": 1604578889, "_nanoseconds": 211000000, "utc": "2020-11-05T12:21:29Z" }, "id": "rhn6u7wwz2WxGv6MZGK9" // Ayrshare Top-Level Post ID }, { "status": "success", "platforms": [ "twitter", "facebook" ], "created": "2022-05-20T17:25:06Z", "post": "Sometimes we need to take a break for lunch.", "scheduleDate": { // In a future release changed to "scheduleDate": "2020-11-05T12:21:29Z" "_seconds": 1604578889, "_nanoseconds": 211000000, "utc": "2020-11-05T12:21:29Z" }, "type": "now", "postIds": [ { "platform": "twitter", "id": "1288890036000983105", // Twitter Social Post ID "status": "success" }, { "id": "104923907983682_108329970009742", // Facebook Social Post ID "status": "success", "platform": "facebook", "isVideo": true // Video post } ], "errors": [], "urls": [], "id": "wWIY0OEirdNeYSJYm1Xa" // Ayrshare Post ID }, { // Awaiting approval post - Approved by user "approved": true, "approvedBy": "9abf1426d6ce9122ef11c7222e1", "approvedDate": "2025-06-06T12:28:12Z", "created": "2025-06-06T12:27:56Z", "errors": [], "id": "sujQsrXtroJU0NEOlY38", "mediaUrls": [], "platforms": [ "twitter" ], "post": "I failed my way to success. - Thomas Edison", "postIds": [ { "status": "success", "id": "193096515330813", "postUrl": "https://twitter.com/RetiretyHQ/status/19309651533081", "platform": "twitter" } ], "profileTitle": "Primary Profile", "refId": "9abf1426d6ce9122ef11c7222e1", "requiresApproval": true, "scheduleDate": "2025-06-06T12:27:56Z", "shortenLinks": false, "status": "success", "type": "now" }, { // Awaiting approval post - Rejected by user "approved": false, "created": "2025-06-06T12:23:48Z", "id": "XL5xHeNK8HGTg07qxzmd", "mediaUrls": [], "platforms": [ "twitter" ], "post": " Honesty is the first chapter in the book of wisdom. - Thomas Jefferson", "profileTitle": "Primary Profile", "refId": "9abf1426d6ce9122ef11c72bd62eddw2", "rejectedBy": "9abf1426d6ce9122ef11c7222e1", "rejectedDate": "2025-06-06T12:23:58Z", "requiresApproval": true, "scheduleDate": "2025-06-06T12:23:48Z", "shortenLinks": false, "status": "awaiting approval", "type": "now" } ], "refId": "9abf1426d6ce9122ef11c72bd62e59807c5cc083", "count": 100, "lastUpdated": "2025-04-05T22:44:14.209Z", "nextUpdate": "2025-04-05T22:45:14.209Z" } ``` ```json 400: History not found theme={"system"} { "action": "history", "status": "error", "code": 221, "message": "History not found for the past 30 days. Please see the docs on how to retrieve additional history. .../ayrshare.com/rest-api/endpoints/history#list-history-of-sent-and-scheduled-posts" } ``` # Post History by ID Source: https://www.ayrshare.com/docs/apis/history/get-history-id GET /history/:id Get the history for a specific post using its Ayrshare Post ID, with an optional searchAllPosts query parameter to search across all User Profiles. > [!NOTE] > > **Targeting X/Twitter?** Starting March 31, 2026, all X operations require your own API credentials. After linking X via OAuth, include these 2 headers in your request: > > `X-Twitter-OAuth1-Api-Key` — Your API Key (Consumer Key) > > `X-Twitter-OAuth1-Api-Secret` — Your API Key Secret (Consumer Secret) > > **One-time setup per Ayrshare account.** You create one X Developer App and reuse the same API Key and Secret across every sub-profile / end-user you link. You do *not* create a new app per customer. > > Not linked yet? See the [full setup guide](/docs/dashboard/connect-social-accounts/x-twitter-byo-keys) to connect your X account. > > Your keys are never logged or stored by Ayrshare. Get the history for a specific post. Replace `:id` with the [Ayrshare Post ID](/docs/apis/overview#ayrshare-post-id) returned from [post](/docs/apis/post/post). ## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` **Profile-Key** `string` [Profile Key](/docs/apis/overview#profile-key-format) of a User Profile. Format: `Profile-Key: PROFILE_KEY` ## Path Parameters **id** `string` *required* Ayrshare Post ID returned from /post ## Query Parameters **searchAllPosts** `boolean` (default: false) Search all posts across all User Profiles for the post ID. Default: `false` ```javascript cURL theme={"system"} curl \ -H "Authorization: Bearer API_KEY" \ -X GET https://api.ayrshare.com/api/history/TBEAAqAMMJoweA9wKHUl ``` ```javascript JavaScript theme={"system"} const API_KEY = "API_KEY"; fetch("https://api.ayrshare.com/api/history/TBEAAqAMMJoweA9wKHUl", { method: "GET", headers: { "Authorization": `Bearer ${API_KEY}` } }) .then((res) => res.json()) .then((json) => console.log(json)) .catch(console.error); ``` ```python Python theme={"system"} import requests headers = {'Authorization': 'Bearer API_KEY'} r = requests.get('https://api.ayrshare.com/api/history/TBEAAqAMMJoweA9wKHUl', headers=headers) print(r.json()) ``` ```php PHP theme={"system"} true, CURLOPT_HTTPHEADER => $headers ]); $response = curl_exec($curl); if ($response === false) { echo 'Curl error: ' . curl_error($curl); } else { echo json_encode(json_decode($response), JSON_PRETTY_PRINT); } curl_close($curl); ``` ```csharp C# theme={"system"} using System; using System.Net.Http; using System.Threading.Tasks; namespace HistoryGETByIDRequest_csharp { class HistoryGetById { static async Task Main(string[] args) { string API_KEY = "API_KEY"; string url = "https://api.ayrshare.com/api/history/TBEAAqAMMJoweA9wKHUl"; using (var client = new HttpClient()) { client.DefaultRequestHeaders.Add("Authorization", "Bearer " + API_KEY); try { var response = await client.GetStringAsync(url); Console.WriteLine(response); } catch (HttpRequestException ex) { Console.WriteLine($"Error: {ex.Message}"); } } } } } ``` ```json 200: Success theme={"system"} { "tier": "business", "status": "success", "mediaUrls": [ "https://images.ayrshare.com/imgs/GhostBusters.jpg" ], "postIds": [ { "platform": "facebook", "postUrl": "https://www.facebook.com/1105775157895689_361710168628052", "status": "success", "id": "105775157895689_361710168628052" // Facebook Social Post ID } ], "id": "TBEEAqAMMJoweA8wKHUp", // Ayrshare Post ID "errors": [], "platforms": [ "facebook" ], "scheduleDate": { // In a future release changed to "scheduleDate": "2020-11-05T12:21:29Z" "_seconds": 1604578889, "_nanoseconds": 211000000, "utc": "2020-11-05T12:21:29Z" }, "createDate": { // deprecated, use created "_seconds": 1653067506, "_nanoseconds": 179000000, "utc": "2022-05-20T17:25:06Z" }, "created": "2022-05-20T17:25:06Z", "shortenLinks": true, "post": "Today is a great day", "notes": "Approved by John Smith", // reference notes set via /post "type": "scheduled" } ``` ```json 400: Post not found theme={"system"} { "action": "history", "status": "error", "code": 221, "message": "History not found.", "id": "4W3f3RPr6QSrw8S5Yo8" } ``` # Get post history for a social platform Source: https://www.ayrshare.com/docs/apis/history/history-platform GET /history/:platform Fetch posts and analytics from Bluesky, Facebook, Instagram, LinkedIn, Pinterest, Snapchat, Threads, TikTok, X, and YouTube, including non-Ayrshare posts. > [!NOTE] > > **Targeting X/Twitter?** Starting March 31, 2026, all X operations require your own API credentials. After linking X via OAuth, include these 2 headers in your request: > > `X-Twitter-OAuth1-Api-Key` — Your API Key (Consumer Key) > > `X-Twitter-OAuth1-Api-Secret` — Your API Key Secret (Consumer Secret) > > **One-time setup per Ayrshare account.** You create one X Developer App and reuse the same API Key and Secret across every sub-profile / end-user you link. You do *not* create a new app per customer. > > Not linked yet? See the [full setup guide](/docs/dashboard/connect-social-accounts/x-twitter-byo-keys) to connect your X account. > > Your keys are never logged or stored by Ayrshare. This endpoint allows you to fetch both posts and their analytics from major social networks (Bluesky, Facebook, Instagram, LinkedIn, Pinterest, Threads, TikTok, X/Twitter, and YouTube). It works for all posts on these platforms - whether they were created using Ayrshare's API or posted directly through the social network's interface. More detailed analytics are available with the [analytics endpoint](/docs/apis/analytics/overview). The IDs returned in from this endpoint are the social networks' native [social post ID](/docs/apis/overview#social-post-id) and not the [Ayrshare post ID](/docs/apis/overview#ayrshare-post-id). This allows you to get details for any post on a social network, even if it was not created through Ayrshare. For example, you can retrieve a post that was manually published on facebook.com and use the Facebook [Social Post ID](/docs/apis/overview#social-post-id) returned to [get comments](/docs/apis/comments/get-comments) or [get analytics](/docs/apis/analytics/social-by-id). If you do not need posts that originated outside of Ayrshare, simply use the [Ayrshare post ID](/docs/apis/overview#ayrshare-post-id) returned in a post with the [comment](/docs/apis/comments/post-comment), [analytics](/docs/apis/analytics/post), or [history](/docs/apis/history/history-social-id) endpoints. `:platform` = `bluesky`, `facebook`, `instagram`, `linkedin`, `pinterest`, `snapchat`, `threads`, `tiktok`, `twitter`, `youtube`. Example: `https://api.ayrshare.com/api/history/instagram` ### Copyrighted Media Instagram and TikTok will exclude all copyrighted media in their responses.
    • The Instagram `mediaUrl` field is omitted from response if the media contains copyrighted material or has been flagged for a copyright violation. Examples of copyrighted material can include audio on reels. You can check in the Instagram app under Settings -> Account Status.
    • TikTok does not return media if the media contains copyrighted material or has been flagged for a copyright violation. Examples of copyrighted material can include audio on reels (TikTok will mute these videos). You can check in the TikTok app under Activity -> System Notifications. If such a video is muted, you can add audio in the TikTok app. Go to the post and select the option to add non-copyrighted audio. The video will then be returned in this API response.
    ### Limitations #### Facebook
    • Expired/archived Stories (older than 24 hours) are automatically filtered out. Only active Stories are included in the response by default.
    • Use the `dataType` query parameter to request only `posts` or only `stories`.
    • Use the `since` and `until` query parameters to filter posts by date range.
    #### Instagram
    • Responses will not include Live Video stories.
    • Stories are only available for 24 hours.
    • New stories created when a user reshares a story will not be returned. Reshared stories include stories created using the Add Yours Templates.
    • New posts created when a user accepts collaboration requests will not be returned.
    • Use the `dataType` query parameter to request only `posts` or only `stories`.
    #### Snapchat
    • Story and Saved Story items include a `mediaUrls` array with the downloadable media URL of each Snap in that Story. Spotlights are single-asset and do not include `mediaUrls`.
    • Fetching per-Snap media requires an additional request per Story, which adds latency. Set the `media` query parameter to `false` to skip it and return Story/Saved Story items without `mediaUrls` for faster, metadata-only responses. Example: `GET https://api.ayrshare.com/api/history/snapchat?media=false`.
    ## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` **Profile-Key** `string` [Profile Key](/docs/apis/overview#profile-key-format) of a User Profile. Format: `Profile-Key: PROFILE_KEY` ## Path Parameters **platform** `string` *required* The platform of the posts to retrieve. Values: `bluesky`, `facebook`, `instagram`, `linkedin`, `pinterest`, `snapchat`, `threads`, `tiktok`, `twitter`, `youtube`. ## Query Parameters **limit** `number` (default: 10) Number of history records to return. Max limit: 500\* High limits may result in slower response times, so we recommend using a limit of no more than 100. \*Higher limits are available on Enterprise plans. Please contact your account manager for details. **skipAnalytics** `boolean` (default: false) Skip gathering full analytics for Facebook Pages and Instagram. Returns only the Social Post ID. Should be used if analytics aren't needed (only need the Social Post ID), for a faster return, or errors occur when limit > 100. **pagePublished** `boolean` (default: 10) Facebook only. By default the posts returned are from the Facebook Page feed. You may also choose to only return posts published by the page. Use feed (default) when you want a more comprehensive view of all content associated with the page, including interactions from other users. Use `pagePublished` when you only want to retrieve posts made by the page itself. **userId** `string` X/Twitter only. This parameter allows you to retrieve posts from a specific X/Twitter user by their numeric ID, rather than from your linked account. For example, to get all posts from the handle `@Google`, you would use their numeric userId `20536157`. You can find any X/Twitter user's numeric userId by using the [Brands Get User](/docs/apis/listen/brand-user) endpoint. Note: Use only the API KEY in the header to make this request. Do not include the Profile Key. **userName** `string` X/Twitter only. This parameter allows you to retrieve posts from a specific X/Twitter user by their handle, rather than from your linked account. For example, to get all posts from the handle `@Google`. Note: Use only the API KEY in the header to make this request. Do not include the Profile Key. **next** `string` Cursor-based pagination for retrieving the next page of results. Pass the `next` value from the previous response's `meta.pagination` object to fetch the next set of posts. **since** `string` Facebook only. ISO UTC date string to filter posts created on or after this date. Use with `until` for a specific date range. Example: `since=2026-03-17` **until** `string` Facebook only. ISO UTC date string to filter posts created on or before this date. Use with `since` for a specific date range. Example: `until=2026-03-20` **dataType** `string` Facebook and Instagram. Filter the type of content returned. By default, both regular posts and active Stories are returned. Values: * `posts` — regular posts only (no Stories) * `stories` — Stories only Omit this parameter to return both posts and Stories (default behavior). Note: For Facebook, expired/archived Stories (older than 24 hours) are automatically filtered out. For Instagram, Stories are only available for 24 hours. **media** `boolean` (default: true) Snapchat only. When `true` (default), Story and Saved Story history items include a `mediaUrls` array with each Snap's downloadable media URL. Fetching that media requires an extra request per Story, so set `media=false` to skip it for faster responses when you only need the Story metadata. ## Pagination Response When using cursor-based pagination (with `next` query parameter), the response includes a `meta.pagination` object containing pagination state. The `posts` array in each paginated response contains objects with the same structure as shown in the response examples below for each platform. **meta.pagination** `object` Pagination metadata for cursor-based navigation through results. **properties** **hasMore** `boolean` Indicates whether there are more results available beyond the current page. When `true`, use the `next` cursor to fetch additional posts. **next** `string` An opaque cursor string to pass as the `next` query parameter to retrieve the next page of results. Only present when `hasMore` is `true` or when there are more results to fetch. **limit** `number` The number of results requested per page. ## Partial Results Retrieving history from some platforms takes several requests behind the scenes. If one of those requests fails part-way through, the posts already retrieved are still returned — but the list is **incomplete**, and the response says so with `status: "partial"` instead of `"success"`. **status** `string` `"success"` when the full requested history was retrieved, or `"partial"` when only some of it was. A `"partial"` response is not an error: the `posts` it contains are accurate, there are simply fewer of them than exist. > [!WARNING] > > Treat `"partial"` as "this page is incomplete", not as "this is everything". Counting posts, or deriving totals from a `"partial"` response, will understate the real figures. Request again to get the full history — partial responses are never cached, so a retry always re-reads from the platform. ```json Partial response theme={"system"} { "status": "partial", "posts": [ { "id": "...", "postUrl": "..." } ], "postsCount": 12, "lastUpdated": "2026-08-12T09:14:02.118Z", "nextUpdate": "2026-08-12T10:14:02.118Z" } ``` ```javascript cURL theme={"system"} curl \ -H "Authorization: Bearer API_KEY" \ -X GET https://api.ayrshare.com/api/history/instagram ``` ```bash cURL Facebook with Filters theme={"system"} # Get only regular posts (no Stories) within a date range curl \ -H "Authorization: Bearer API_KEY" \ -X GET "https://api.ayrshare.com/api/history/facebook?limit=20&since=2026-03-17&until=2026-03-20&dataType=posts" ``` ```bash cURL with Pagination theme={"system"} # First request curl \ -H "Authorization: Bearer API_KEY" \ -X GET "https://api.ayrshare.com/api/history/twitter?limit=10" # Next page request (use 'next' cursor from previous response) curl \ -H "Authorization: Bearer API_KEY" \ -X GET "https://api.ayrshare.com/api/history/twitter?limit=10&next=eyJ0b2tlbiI6IjE3MzkyNjg1MTQ0ODU3MTUi..." ``` ```javascript JavaScript theme={"system"} const API_KEY = "API_KEY"; fetch("https://api.ayrshare.com/api/history/instagram", { method: "GET", headers: { "Authorization": `Bearer ${API_KEY}` } }) .then((res) => res.json()) .then((json) => console.log(json)) .catch(console.error); ``` ```javascript JavaScript with Pagination theme={"system"} const API_KEY = "API_KEY"; // Function to fetch posts with pagination (up to maxPosts or until cutoffTime) async function fetchPosts(platform, { limit = 25, maxPosts = 100, cutoffTime = null } = {}) { const posts = []; let nextCursor = null; let pagePosts = []; do { const url = new URL(`https://api.ayrshare.com/api/history/${platform}`); url.searchParams.set("limit", limit); if (nextCursor) { url.searchParams.set("next", nextCursor); } const response = await fetch(url, { method: "GET", headers: { "Authorization": `Bearer ${API_KEY}` } }); const data = await response.json(); pagePosts = data.posts || []; // Filter by date if cutoffTime is specified if (cutoffTime) { pagePosts = pagePosts.filter(post => new Date(post.created) >= cutoffTime); } posts.push(...pagePosts); // Get cursor for next page nextCursor = data.meta?.pagination?.hasMore ? data.meta.pagination.next : null; } while (nextCursor && pagePosts.length > 0 && posts.length < maxPosts); return posts.slice(0, maxPosts); } // Fetch up to 100 Twitter posts fetchPosts("twitter", { maxPosts: 100 }).then(posts => console.log(posts)); // Fetch posts from the last 30 days (up to 200) const thirtyDaysAgo = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000); fetchPosts("twitter", { maxPosts: 200, cutoffTime: thirtyDaysAgo }).then(posts => console.log(posts)); ``` ```python Python theme={"system"} import requests headers = {'Authorization': 'Bearer API_KEY'} r = requests.get('https://api.ayrshare.com/api/history/instagram', headers=headers) print(r.json()) ``` ```php PHP theme={"system"} true, CURLOPT_HTTPHEADER => $headers ]); $response = curl_exec($curl); if ($response === false) { echo 'Curl error: ' . curl_error($curl); } else { echo json_encode(json_decode($response), JSON_PRETTY_PRINT); } curl_close($curl); ``` ```csharp C# theme={"system"} using System; using System.Net.Http; using System.Threading.Tasks; namespace HistoryPlatformGETRequest_csharp { class HistoryPlatform { static async Task Main(string[] args) { string API_KEY = "API_KEY"; string url = "https://api.ayrshare.com/api/history/TBEAAqAMMJoweA9wKHUl"; using (var client = new HttpClient()) { client.DefaultRequestHeaders.Add("Authorization", "Bearer " + API_KEY); try { var response = await client.GetStringAsync(url); Console.WriteLine(response); } catch (HttpRequestException ex) { Console.WriteLine($"Error: {ex.Message}"); } } } } } ``` ```json 200: Success {4, 23, 161, 216, 287, 367, 488, 672} theme={"system"} { "status": "success", "posts": [ // Bluesky Response Example { "cid": "bafyreigh7nz7x6pl4atgsextqtcbh33mg66yqt3caeihutn7ojkmpdtnzm", // Bluesky Content ID "created": "2025-01-06T21:35:08.951Z", "id": "at://did:plc:n7atrjd22xgkmgwig6dzlhzd/app.bsky.feed.post/3lfb57nxgxs2e", // Bluesky Social Post ID "indexedAt": "2025-01-06T21:35:09.156Z", "labels": [], "likeCount": 2, "post": "What a wonderful data day!", "postUrl": "https://bsky.app/profile/ayrshare.com/post/3lf43nl5jis24", "quoteCount": 3, "replyCount": 2, "repostCount": 1, "viewer": { "threadMuted": false, "embeddingDisabled": false } }, // Facebook Response Example // Note: If the FB page has fewer than 100 likes then not all analytics data available. { "clicksUnique": 31, "created": "2022-06-14T23:43:21Z", "commentsCount": 13, "engagedUsers": 3, "fullPicture": "https://scontent.ford4-1.fna.fbcdn.net/v/t39.30808-6/287997502_758105082307121_n.jpg?stp=dst-jpg_s720x720&_nc_cat=101&ccb=1-7&_nc_sid=8024bb&_nc_ohc=EkktQTp_uWIAX_xtTxV&_nc_ht=scontent.ford4-1.fna&edm=AKK4YLsEAAAA&oh=00_AT-Y5Ht2Pl3MeY-qIBs6BC10SbZ-47Vfcc7DKiQrCnIUA&oe=62AEEECA", "id": "104619420979033_758005082307127", // Facebook Social Post ID "impressionsUnique": 0, "isPopular": false, "lastUpdated": "2022-06-14T23:43:38.359Z", "likeCount": 38, "likedBy": [ // Users who liked the post { "id": "7101149746568432", "name": "John Smith" } ], "mediaUrls": [ // If attached photos or video { "media": { "image": { "height": 412, "src": "https://scontent-lga3-1.xx.fbcdn.net/v/t15.5256-10/294978304_1014487762575117_6279324362130508123_n.jpg?stp=dst-jpg_s720x720&_nc_cat=101&ccb=1-7&_nc_sid=ad6a45&_nc_ohc=y3bYN-PGN0YAX9kO2qn&_nc_ht=scontent-lga3-1.xx&edm=AKK4YLsEAAAA&oh=00_AT9by7D4y4sjY4BW5RW53JO6BaG2UkpUCCJrrugW7GDW9w&oe=62E23000", "width": 720 }, // Source if mediaType is video "source": "https://video-lga3-1.xx.fbcdn.net/v/t39.25447-2/294718581_137662848672158_7246454806912028286_n.mp4?_nc_cat=111&vs=696d8a2ba08f208b&_nc_vs=HBkcFQAYJEdIVU1rUkdlb1RFaE5IMEFBSDdPNmVaQWxKQmtibWRqQUFBRhUAAsgBAEsGiBJwcm9ncmVzc2l2ZV9yZWNpcGUBMQ1zdWJzYW1wbGVfZnBzABB2bWFmX2VuYWJsZV9uc3ViACBtZWFzdXJlX29yaWdpbmFsX3Jlc29sdXRpb25fc3NpbQAoY29tcHV0ZV9zc2ltX29ubHlfYXRfb3JpZ2luYWxfcmVzb2x1dGlvbgARZGlzYWJsZV9wb3N0X3B2cXMAFQAlABwAACac%2FNfk2tKqARWQTigCQzMYC3Z0c19wcmV2aWV3HBdAGZmZmZmZmhhEZGFzaF9pNGxpdGViYXNpY19wYXNzdGhyb3VnaGFsaWduZWRfNDgwX2NyZl8yOF9tYWluXzMuMF9mcmFnXzJfdmlkZW8SABgYdmlkZW9zLnZ0cy5jYWxsYmFjay5wcm9kOBJWSURFT19WSUVXX1JFUVVFU1QbD4gVb2VtX3RhcmdldF9lbmNvZGVfdGFnBm9lcF9zZBNvZW1fcmVxdWVzdF90aW1lX21zATAMb2VtX2NmZ19ydWxlCnNkX3VubXV0ZWQTb2VtX3JvaV9yZWFjaF9jb3VudAI5OBFvZW1faXNfZXhwZXJpbWVudAAMb2VtX3JvaV9ub3RlC3Byb2dyZXNzaXZlEW9lbV9yb2lfdXNlcl90aWVyAB5vZW1fcm9pX3ByZWRpY3RlZF93YXRjaF90aW1lX3MBMBZvZW1fcm9pX3JlY2lwZV9iZW5lZml0BTAuMDAwJW9lbV9yb2lfc3RhdGljX2JlbmVmaXRfY29zdF9ldmFsdWF0b3ILcHJvZ3Jlc3NpdmUMb2VtX3ZpZGVvX2lkEDEyNjk2MTcwNzM3NzgxNTESb2VtX3ZpZGVvX2Fzc2V0X2lkDzc5ODQ2NjE3NDg0NzE4OBVvZW1fdmlkZW9fcmVzb3VyY2VfaWQPMzc1MjU0ODg3ODkwNzAyHG9lbV9zb3VyY2VfdmlkZW9fZW5jb2RpbmdfaWQPNDUwODU0MzgzNjE1MDc5DnZ0c19yZXF1ZXN0X2lkD2EyYThlNWU4OTBkNTRmMyUCHBwcFfDmFxsBVQACGwFVAAIcFQIAAAAWgLq3AwAlxAEbB4gBcwQ1ODkyAmNkCjIwMjItMDctMjQDcmNiATADYXBwBVdhdGNoAmN0GERJUkVDVEVEX1BPU1RfQVRUQUNITUVOVBNvcmlnaW5hbF9kdXJhdGlvbl9zBTYuNDE3AnRzFHByb2dyZXNzaXZlX29yZGVyaW5nAA%3D%3D&ccb=1-7&_nc_sid=a06cc9&_nc_ohc=bA3WO4Dk72QAX9TB9ag&_nc_ht=video-lga3-1.xx&edm=AKK4YLsEAAAA&oh=00_AT_CzNhlIwNxY_7UMWIGQMNOtD-Aw_LGcVHnDWvcQJVkAw&oe=62E2ADE1&_nc_rid=780575090331889" }, "mediaType": "video", // "link" or "photo" "url": "https://www.facebook.com/115237020273490/videos/1269617073778151", "videoId": "1269617073778151" } ], "negativeFeedback": 0, "negativeFeedbackUnique": 0, "nextUpdate": "2022-06-15T00:18:38.359Z", "parentId": "5764771300200609_742258953848744", // If a shared post, then the ID of the original post "post": "What a nice one", "postUrl": "https://www.facebook.com/104619420979033_758105082307121", "properties": [ // for videos only { "name": "Length", "text": "00:07" } ], "reactions": { // Total lifetime "like": 1, // Like reactions - The "like" reaction counts include both "like" and "care" reactions. "love": 1, // Love reactions "anger": 1, // Anger reactions "haha": 1, // Haha reactions "wow": 1, // Wow reactions "sorry": 1, // Sorry reactions "total": 6 // Total number of reactions }, "reactionsByType": 1, "statusType": "added_video", // Status actions: added_photos, added_video, app_created_story, approved_friend, created_event, created_group, created_note, mobile_status_update, published_story, shared_story, tagged_in_photo, wall_post "videoViewTime": 0, "videoViews": 0, "videoViewsUnique": 0 }, { "clicksUnique": 45, "created": "2022-06-14T14:53:01Z", "engagedUsers": 2, "fullPicture": "https://external.ford4-1.fna.fbcdn.net/emg1/v/t13/614683800928174083?url=https%3a%2f%2fwww.alphast.com%2fwp-content%2fuploads%2f2022%2f06%2fheadphones-scaled.jpg&fb_obo=1&utld=fbcdn.net&stp=dst-emg0_q75&ccb=13-1&oh=00_AT-MVM6lP-eWgsqCW6IhImKAJaGy71JIGIntjlVnZkJw&oe=62AAAC5C&_nc_sid=5f3a21", "id": "104619420979033_757752962332339", // Facebook Social Post ID "impressionsUnique": 1, "isPopular": false, "lastUpdated": "2022-06-14T23:40:48.453Z", "mediaUrls": [ { "mediaUrl": "link", "url": "https://www.alphasht.com/finding-the-best-headphones/" } ], "negativeFeedback": 0, "negativeFeedbackUnique": 0, "nextUpdate": "2022-06-15T00:15:48.453Z", "post": "Finding The Best Headphones: Finding the best headphones for you depends on several factors.", "postUrl": "https://www.facebook.com/104619420979033_75775296233233", "reactions": { // Total lifetime "like": 1, // Like reactions - The "like" reaction counts include both "like" and "care" reactions. "love": 1, // Love reactions "anger": 1, // Anger reactions "haha": 1, // Haha reactions "wow": 1, // Wow reactions "sorry": 1, // Sorry reactions "total": 6 // Total number of reactions }, "reactionsByType": 1, "videoViewTime": 0, "videoViews": 0, "videoViewsUnique": 0 }, { // Facebook Story "clicksUnique": 0, "commentsCount": 0, "created": "2024-03-15T19:36:29.000Z", "engagedUsers": 0, "id": "1593984754722429", "impressionsFanPaidUnique": 0, "impressionsFanUnique": 0, "impressionsOrganicUnique": 0, "impressionsPaidUnique": 0, "impressionsUnique": 0, "likeCount": 0, "mediaId": "1593984754722429", "mediaType": "video", "mediaUrls": [ { "media": { "image": { "height": 720, "src": "https://scontent-lga3-1.xx.fbcdn.net/v/t15.5256-10/451306699", "width": 405 }, "source": "https://video-lga3-1.xx.fbcdn.net/o1/v/t2/f2/m69/An_VRpjrlZJxZEUVTHHMpd" }, "mediaType": "video", "type": "story" } ], "negativeFeedback": 0, "negativeFeedbackUnique": 0, "post": "", "postId": "1433249613981178", "postUrl": "https://facebook.com/stories/114251701259415/UzpfSVNDOjE0MzMyNDk2MTczMTQ1MTE=/?view_single=1", "reactions": {}, "reactionsByType": 0, "sharesCount": 0, "statusType": "published", "videoViewTime": 0, "videoViews": 0, "videoViewsUnique": 0 }, // Instagram Response Example { "mediaUrl": "https://scontent.cdninstagram.com/v/t51.2885-15/75317300_249468363049056_6683482523516619964_n.jpg", "permalink": "https://www.instagram.com/p/B5OBT3ygpfg/", "commentsCount": 0, "created": "2022-05-20T17:26:03Z", "likeCount": 0, "mediaProductType": "FEED", // Media product type: AD, FEED, STORY or REELS "mediaType": "IMAGE", // Media type: CAROUSEL_ALBUM, IMAGE, or VIDEO "username": "thegoodone", "id": "17833140557332933", // Instagram Social Post ID "thumbnailUrl": "https://scontent-lga3-2.cdninstagram.com/v/t51.2885-15/277243581_1136333620462129_9111753545576538669_n.jpg", "post": "The #Mandalorian is on tonight instead of Friday", "postUrl": "https://www.instagram.com/p/B5OBT3ygpfg/" // same as permalink }, { "mediaUrl": "https://scontent.cdninstagram.com/v/t51.2885-15/72719989_401282024671549_6704610247561386099_n.jpg", // The media_url field is omitted from responses if the media contains copyrighted material or has been flagged for a copyright violation. Examples of copyrighted material can include audio on reels. "permalink": "https://www.instagram.com/p/B6_EubJFlMI/", "commentsCount": 0, "created": "2022-05-20T17:26:03Z", "likeCount": 0, "mediaProductType": "REELS", // Media product type: AD, FEED, STORY or REELS "mediaType": "VIDEO", // Media type: CAROUSEL_ALBUM, IMAGE, or VIDEO "username": "thegoodone", "id": "17933140557332933", // Instagram Social Post ID "thumbnailUrl": "https://scontent-lga3-2.cdninstagram.com/v/t51.2885-15/277243581_1136333620462129_9111753545576538669_n.jpg", "post": "And finally, number eleven of the best shows of 2019 is #Mandalorian #BabyYoda #Bestof2019", "postUrl": "https://www.instagram.com/p/B6_EubJFlMI/" // same as permalink }, { "commentsCount": 3, "created": "2024-04-29T16:10:10Z", "id": "17998949393616", "isPopular": false, "lastUpdated": "2024-04-29T22:28:20.499Z", "likeCount": 14, "mediaProductType": "FEED", "mediaType": "CAROUSEL_ALBUM", "mediaUrls": [ { "mediaUrl": "https://scontent-lga3-2.cdninstagram.com/v/t51.2885-15/441166659", "id": "18395216701078298" }, { "mediaUrl": "https://scontent-lga3-2.cdninstagram.com/o1/v/t16/f1/m82/834208F324481538FE6E565772BC6B8E_video_dashinit.mp4", "thumbnailUrl": "https://scontent-lga3-2.cdninstagram.com/v/t51.29350-15/440150892_371703612533736_7866608290099350261_n.jpg", "id": "18041898211827161" } ], "nextUpdate": "2024-04-29T22:39:20.499Z", "post": "Learn to value yourself, which means: to fight for your happiness. - Ayn Rand", "postUrl": "https://www.instagram.com/p/C6WgGmSPn/", "username": "johnboy" }, // LinkedIn Response Example { "clickCount": 0, "commentCount": 1, "created": "2022-12-28T16:07:38Z", "engagement": 0.6666666666666666, "id": "urn:li:share:701389826709", // LinkedIn Social Post ID "impressionCount": 3, "lastModified": "2022-12-28T16:07:38Z", "likeCount": 1, "post": "left thirty pocket track flower whistle", "postUrl": "https://www.linkedin.com/feed/update/urn:li:share:701389826709", "publishedAt": "2022-12-28T16:07:38Z", "reactions": { // Get reactions on a LinkedIn share "like": 1, // "Like in the UI "praise": 2, // "Celebrate" in the UI "maybe": 3, // "Curious" in the UI "empathy": 3, // "Love" in the UI "interest": 2 // "Insightful" in the UI "appreciation": 5 // "Support" in the UI }, "shareCount": 0, "status": "PUBLISHED", "uniqueImpressionsCount": 3, "visibility": "PUBLIC" }, { "clickCount": 0, "comments": [ "urn:li:comment:(urn:li:activity:722)" // Look up using the Comments endpoint ], "commentCount": 1, "commentsState": "OPEN", "created": "2022-12-28T17:05:11Z", "engagement": 0.16, // Not available for Personal accounts "id": "urn:li:share:70139127498", // LinkedIn Social Post ID "impressionCount": 12, // Not available for Personal accounts "lastModified": "2022-12-28T17:05:11Z", "likeBy": [ "urn:li:person:Sgwlpf744" // Look up using the brands endpoint ], "likeCount": 1, "likedByCurrentUser": false, "mediaUrls": [ // please see .../ayrshare.com/additional-info/upcoming-api-changes#changes-in-effect-december-1-2023 { "id": "urn:li:video:C4E10AQHz0RMm5aAiAg", "url": "https://media.licdn.com/dms/image/C4E10AQHz0RMm5aAiAg" }, { "id": "urn:li:video:C4E10AQHz0RMm5adsH2", "url": "https://media.licdn.com/dms/image/C4E10AQHz0RMm5adsH2" } ], "post": "no monkey gulf organization mood choose earn", "postUrl": "https://www.linkedin.com/feed/update/urn:li:share:70139127498", "publishedAt": "2022-12-28T17:05:11Z", "reactions": { // Get reactions on a LinkedIn share "like": 1, // "Like in the UI "praise": 2, // "Celebrate" in the UI "maybe": 3, // "Curious" in the UI "empathy": 3, // "Love" in the UI "interest": 2 // "Insightful" in the UI "appreciation": 5 // "Support" in the UI }, "shareCount": 0, // Not available for Personal accounts "status": "PUBLISHED", "uniqueImpressionsCount": 9, // Not available for Personal accounts "totalFirstLevelComments": 1, "visibility": "PUBLIC" }, // Pinterest Response Example { "altText": "", "boardId": "955255839651522358", "coverImageUrl": "https://i.pinimg.com/originals/7e/ea/af/7eeaaf4bce167902b06fe3370227e.jpg", "created": "2022-08-09T22:25:12Z", "dominantColor": "#353027", // Dominant pin color. Hex number, e.g. "#6E7874" "duration": 6416, // Time in millisecord "height": 1062, "id": "7184649418547260", // Pinterest Social Post ID "link": "", "mediaType": "video", "mediaUrl": "https://i.pinimg.com/originals/7e/ea/af/7eeaaf4bce167902b06fe3370227e.jpg", "note": "What a great post", "post": "You are what you believe yourself to be. - Paulo Coelho", "postUrl": "https://www.pinterest.com/pin/7184649418547260", "productTags": [], "title": "My new post", "username": "ayrshare", "width": 1856 }, { "altText": "", "created": "2022-08-09T22:25:12Z", "dominantColor": "#7c7c7c", "height": 600, "id": "7184649468512979", // Pinterest Social Post ID "link": "", "mediaType": "image", "mediaUrl": "https://i.pinimg.com/originals/1b/0a/02/1b0a02123fba55042474b61bb2aa1.jpg", "post": "Peace begins with a smile. - Mother Teresa", "postUrl": "https://www.pinterest.com/pin/7184649468512979", "username": "ayrshare", "width": 600 }, { "altText": "", "created": "2022-10-17T12:26:05Z", "dominantColor": "#7f805e", "id": "36493237618990511", // Pinterest Social Post ID "link": "", "mediaType": "multiple_mixed", "mediaUrls": [ { "mediaType": "image", "height": 442, "width": 249, "mediaUrl": "https://i.pinimg.com/originals/6c/e2/1f/6ce21fa3936b51cd39c34be6f60dd469.jpg" }, { "mediaType": "image", "height": 589, "width": 332, "mediaUrl": "https://i.pinimg.com/originals/9370f0845be155b5640c0f.jpg" }, { "mediaType": "image", "height": 589, "width": 332, "mediaUrl": "https://i.pinimg.com/originals/9370f0845be155b5640c0f.jpg" }, { "mediaType": "image", "height": 589, "width": 332, "mediaUrl": "https://i.pinimg.com/originals/9370f0845be155b5640c0f.jpg" }, { "mediaType": "video", "duration": 19600, "height": 1280, "width": 720, "mediaUrl": "https://i.pinimg.com/videos/thumbnails/originals/9370f0845be155b5640c0f.jpg" } ], "post": " ", "postUrl": "https://www.pinterest.com/pin/36493237618990511", "username": "johnsmith" }, // Snapchat Response Example { "id": "43d61bfe-2249-5a46-944a-d36d27c9530b", "profileId": "43548e97-edf1-44f9-984a-0a38470133", "thumbnailUrl": "https://cf-st.sc-cdn.net/d/fY3MDNOXp0hxLecKdr4hV", "type": "PUBLIC_STORY", // PUBLIC_STORY, SAVED_STORY, or SPOTLIGHT "created": "2025-05-19T19:38:36.789Z", "ended": "2025-05-19T19:38:36.790Z", "mediaUrls": [ // per-Snap downloadable media (returned by default; omitted when media=false). Spotlights excluded. "https://cf-st.sc-cdn.net/d/8Yb8LJ5TWb2yVMSTuF6dg.10.IRZXSOY", "https://cf-st.sc-cdn.net/d/AbC123dEfG456hIjK.20.IRZXSOY" ] }, { "id": "07c99fea-1c72-4f72-bcd9-7dacfae753e0", "profileId": "43548e97-edf1-44f9-984a-0a3847023", "thumbnailUrl": "https://cf-st.sc-cdn.net/d/wTf9NQ0mZ4GCDyFj7feQ8", "type": "SAVED_STORY", // PUBLIC_STORY, SAVED_STORY, or SPOTLIGHT "title": "A song about snaps", "created": "2025-05-20T18:09:04.876Z", "updated": "2025-05-20T18:09:04.876Z", "mediaUrls": [ // per-Snap downloadable media (returned by default; omitted when media=false) "https://cf-st.sc-cdn.net/d/wTf9NQ0mZ4GCDyFj7feQ8.30.IRZXSOY" ] }, // TikTok Response Example { "audienceCities": [ { "city_name": "US Teton County", "percentage": 3.1 }, { "city_name": "US Queens", "percentage": 6.3 } ], "audienceCountries": [ { "country": "GB", "percentage": 0.0029 }, { "country": "US", "percentage": 0.9604 } ], "averageTimeWatched": 2.0132, // Average watch time in seconds. The average time viewers spent watching your video. "commentsCount": 1, // Total number of lifetime comments. Available 24-48 hours after posting. "created": "2024-02-26T18:32:00Z", "embedUrl": "https://www.tiktok.com/static/profile-video?id=73399801580586794&", "engagementLikes": [ // Engagement likes. The distribution of your viewers who liked your video at specific points in the video's timeline. { "percentage": 0, "second": "179" }, { "percentage": 0, "second": "339" } ], "fullVideoWatchedRate": 0.0176, // The percentage of viewers who watched the entire video. "id": "7339980158058679594", "impressionSources": [ { "impression_source": "Search", "percentage": 0 }, { "impression_source": "Sound", "percentage": 0 }, { "impression_source": "other_profile_vv", "percentage": 0 }, { "impression_source": "Follow", "percentage": 0 }, { "impression_source": "For You", "percentage": 1 }, { "impression_source": "Hashtag", "percentage": 0 }, { "impression_source": "Personal Profile", "percentage": 0 } ], "likeCount": 2, // Total number of lifetime likes. Available 24-48 hours after posting. "mediaType": "video", "musicTitle": "♬ original sound - Mack", // If available "musicUrl": "https://www.tiktok.com/music/original-sound", // If available "name": "Mack", // If available "post": "I have failed over and over and over again in my life and that is why I succeed. - Michael Jordan", "postUrl": "https://www.tiktok.com/@happy/video/7339980158058679594", "reach": 653, // The number of people who watched your published content at least once. "shareUrl": "https://www.tiktok.com/@happy/video/7339980158058679594", "shareCount": 2, // Total number of lifetime shares. Available 24-48 hours after posting. "tags": [], "thumbnailHeight": 1024, // If available "thumbnailUrl": "https://p19-sign.tiktokcdn-us.com/obj/tos-useast5-p-85c255-tx/oIpT 9CLHAaQfuD67E3jfAFJ5eVDuF5Ibkmszg?x-expires=1709305200&x-signature=ZBTR0Q%2B4KLi8szRCir8eYEcAs1s%3D", "thumbnailWidth": 576, // If available "totalTimeWatched": 1373, // Total watch time in seconds. The total time viewers spent watching your video. "url": "https://www.tiktok.com/@happy", "videoDuration": 11.332, // Video duration in seconds. "videoViewRetention": [ // This metric indicates how many of your viewers are still watching after a certain amount of time. { "percentage": 0.1, "second": "155" }, { "percentage": 0.07, "second": "234" } ], /* Total number of lifetime users who viewed the video. Available 24-48 hours after posting. If the user swipes away from the ad then swipes back, it would be counted as 2 impressions. Therefore, a new video view will be counted again with the new impression session. */ "videoViews": 697 }, // Twitter Response Example // Note: Non-public and organic metrics only available last 30-days of Tweets { "created": "2022-05-20T20:07:53.000Z", "entities": { "urls": [ { "start": 77, // Starting character position (inclusive) "end": 96, // Ending character position (exclusive) "url": "https://t.co/abc123", // Shortened t.co URL "expandedUrl": "https://www.ayrshare.com/docs", // Full URL after redirect "displayUrl": "ayrshare.com/docs", // User-friendly display version "unwoundUrl": "https://www.ayrshare.com/docs" // Final destination URL } ], "hashtags": [ { "start": 97, // Starting character position (inclusive) "end": 112, // Ending character position (exclusive) "tag": "SocialMediaAPI" // Hashtag text without # } ], "mentions": [ { "start": 113, // Starting character position (inclusive) "end": 122, // Ending character position (exclusive) "username": "ayrshare" // Username without @ } ], "cashtags": [ { "start": 123, // Starting character position (inclusive) "end": 128, // Ending character position (exclusive) "tag": "META" // Stock symbol without $ } ], "annotations": [ { "start": 46, // Starting character position (inclusive) "end": 54, // Ending character position (exclusive) "probability": 0.9456, // Confidence score (0.0 to 1.0) "type": "Product", // Entity type: Person, Place, Product, Organization, Other "normalizedText": "Ayrshare" // Standardized entity name } ] }, "id": "1187026023991603", // Twitter Social Post ID "media": [ // Attached media { "mediaKey": "7_1555577959943217152", "previewImageUrl": "https://pbs.twimg.com/ext_tw_video_thumb/1555577959943217152/pu/img/f4M9veIozFjQA_XD.jpg", "durationMs": 2168, // Duration in milliseconds, videos only "type": "video" // or "photo", "animated_gif" } ], "nonPublicMetrics": { // Use this to determine the total number of impressions generated for the Tweet. Only available for Tweets created the last 30 days. "impressionCount": 1, // Number of times the Tweet has been viewed "userProfileClicks": 0 }, "organicMetrics": { // Use this to measure organic engagement for the Tweet. Only available for Tweets created the last 30 days. "userProfileClicks": 2, "impressionCount": 4, // Number of times the Tweet has been viewed organically. "replyCount": 3, "retweetCount": 1, "likeCount": 234 }, "possiblySensitive": false, // Tweet contains possibly sensitive information "post": "Just launched our social media campaign using Ayrshare! Check out the API at https://t.co/abc123 #SocialMediaAPI @ayrshare $META", "postUrl": "https://twitter.com/myhandle/status/1187026023991603", "publicMetrics": { // Use this to measure Tweet engagement. Only available for Tweets created the last 30 days. "retweetCount": 2, "replyCount": 2, "likeCount": 5, "quoteCount": 1 }, "referencedTweets": [ // If a quoted or reply tweet { "type": "quoted", // or "replied_to" "id": "1556718165828276224", // Quoted or replied Tweet ID "url": "https://www.twitter.com/1556718165828276224" // Quoted or Replied Tweet URL } ], "source": "Ayrshare", // Determine if a Twitter user posted from the web, mobile device, or other app. "text": "To effectively communicate, we must realize that we are all different", // Deprecated, use "post" field "urls": [ // Available for long Tweets only { "start": 866, "end": 889, "url": "https://t.co/3r6xz4hBnM", "expandedUrl": "https://www.cnn.com", "displayUrl": "cnn.com" } ], }, { "authorId": "1194338779881472", "created": "2023-12-27T03:24:23.000Z", "createdAt": "2023-12-27T03:24:23.000Z", "editHistoryTweetIds": [ "17398682514485715" ], "id": "17398682514485715", "media": [ // available for videos and gifs { "nonPublicMetrics": { "playback100Count": 2, "playback0Count": 55, "playback50Count": 4, "playback25Count": 11, "playback75Count": 2 }, "publicMetrics": { "viewCount": 27 }, "durationMs": 28240, "organicMetrics": { "playback100Count": 2, "playback0Count": 55, "playback25Count": 11, "playback75Count": 2, "playback50Count": 4, "viewCount": 27 }, "mediaKey": "7_1739849636389814272", "previewImageUrl": "https://pbs.twimg.com/ext_tw_video_thumb/1739849636389814272/pu/img/bCkAdkD0R00-ZlkY.jpg", "width": 1920, "type": "video", "height": 1080, "mediaUrls": [ { "bitRate": 256000, "contentType": "video/mp4", "mediaUrl": "https://video.twimg.com/ext_tw_video/1739849636389814272/pu/vid/avc1/480x270/4RoZCqynednVMFgy.mp4?tag=12" }, { "bitRate": 832000, "contentType": "video/mp4", "mediaUrl": "https://video.twimg.com/ext_tw_video/1739849636389814272/pu/vid/avc1/640x360/RYy5iHTIxu7_iyZm.mp4?tag=12" }, { "bitRate": 2176000, "contentType": "video/mp4", "mediaUrl": "https://video.twimg.com/ext_tw_video/1739849636389814272/pu/vid/avc1/1280x720/9UpR90ekFA-9Ucxo.mp4?tag=12" }, { "contentType": "application/x-mpegURL", "mediaUrl": "https://video.twimg.com/ext_tw_video/1739849636389814272/pu/pl/0AVy2xIOLQcsrEZG.m3u8?tag=12&container=fmp4" } ] } ], "nonPublicMetrics": { "impressionCount": 54, "userProfileClicks": 0 }, "organicMetrics": { "userProfileClicks": 0, "likeCount": 0, "replyCount": 0, "impressionCount": 54, "retweetCount": 0 }, "possiblySensitive": false, "post": "Family is the most important thing in the world. - Diana, Princess of Wales https://t.co/MgYxnJDer6", "postUrl": "https://twitter.com/superme/status/17398682514485715", "publicMetrics": { "retweetCount": 0, "replyCount": 0, "likeCount": 0, "quoteCount": 0, "bookmarkCount": 0, "impressionCount": 54 }, "urls": [ // Available for long Tweets only { "start": 866, "end": 889, "url": "https://t.co/3r6xz4hBnM", "expandedUrl": "https://www.cnn.com", "displayUrl": "cnn.com" } ], }, // YouTube Response Example { "created": "2022-08-15T19:14:44Z", "description": "Doubt is not a pleasant condition, but certainty is absurd. - Voltaire", // Deprecated, use "post" field "id": "Q-4OuHCALVa", // YouTube Social Post ID "position": 0, "post": "Doubt is not a pleasant condition, but certainty is absurd. - Voltaire", "postUrl": "https://www.youtube.com/watch?v=Q-4OuHCALVa", "privacyStatus": "public", "license": "youtube", // "youtube" or "creativeCommon" "embeddable": true, // whether the video can be embedded on external sites "publicStatsViewable": true, // whether the extended stats panel is publicly viewable "published": "2025-05-07T16:27:12Z", "thumbnailUrl": "https://i.ytimg.com/vi/Q-4OuHCALVa/default.jpg", "title": "Yo time" }, { "created": "2022-08-08T22:24:42Z", "description": "Little author little, one travels far. - J.R.R. Tolkien", // Deprecated, use "post" field "id": "Btg4ysKnHea", // YouTube Social Post ID "position": 1, "post": "Little author little, one travels far. - J.R.R. Tolkien", "postUrl": "https://www.youtube.com/watch?v=Btg4ysKnHea", "privacyStatus": "unlisted", "license": "creativeCommon", "embeddable": false, "publicStatsViewable": false, "published": "2025-05-07T16:27:12Z", "thumbnailUrl": "https://i.ytimg.com/vi/Btg4ysKnHea/default.jpg", "title": "Nice one 2" } ], "lastUpdated": "2022-11-14T16:04:51.994Z", "nextUpdate": "2022-11-14T16:37:21.994Z", "meta": { "pagination": { "hasMore": true, // More results available "next": "eyJ0b2tlbiI6IjE3MzkyNjg1MTQ0ODU3MTUiLCJzb3VyY2UiOiJwdWJsaWMiLCJwbGF0Zm9ybSI6InR3aXR0ZXIifQ==", // Cursor for next page "limit": 10 // Requested page size } } } ``` ```json 400: Error theme={"system"} { "status": "error", "code": 196, "message": "Instagram is not connected" } ``` ```json 400: Bad Request theme={"system"} { // When some of the data is not available from the social networks. // All available data is still returned "status": "error", "posts": [ {... valid data }, { "action": "analytics", "code": 187, "commentsCount": 0, "created": "2019-03-22T13:31:02Z", "createdTime": "2019-03-22T13:31:02+0000", "from": { "name": "Ayrshare", "id": "746346376697" }, "fullPicture": "https://external.fphl1-1.fna.fbcdn.net/emg1/v/t13/", "id": "746346376697_3993223", // Facebook Social Post ID "isPopular": false, "likeCount": 2, "mediaUrls": [ { "description": "Dirk Cotton's approach to retirement planning.", "media": { "image": { "height": 720, "src": "https://external.fphl1-1.fna.fbcdn.net/emg1/v/t13/608115669879219", "width": 720 } }, "mediaType": "link", "title": "The Retirement Planning Regime", "url": "https://bit.ly/2RwaH" } ], "message": "Error getting analytics.", "post": "Error getting analytics.", "status": "error", "statusType": "shared_story" }, {... valid data } ] } ``` # Posts History by Social ID Source: https://www.ayrshare.com/docs/apis/history/history-social-id GET /history/:socialId Retrieve history for posts that did not originate via Ayrshare using the Social Post ID, with the searchPlatformId and platform query parameters. > [!NOTE] > > **Targeting X/Twitter?** Starting March 31, 2026, all X operations require your own API credentials. After linking X via OAuth, include these 2 headers in your request: > > `X-Twitter-OAuth1-Api-Key` — Your API Key (Consumer Key) > > `X-Twitter-OAuth1-Api-Secret` — Your API Key Secret (Consumer Secret) > > **One-time setup per Ayrshare account.** You create one X Developer App and reuse the same API Key and Secret across every sub-profile / end-user you link. You do *not* create a new app per customer. > > Not linked yet? See the [full setup guide](/docs/dashboard/connect-social-accounts/x-twitter-byo-keys) to connect your X account. > > Your keys are never logged or stored by Ayrshare. Retrieve history for posts that did not originate via Ayrshare by providing the low-level Social post ID. This [Social Post ID ](/docs/apis/overview#social-post-id)is returned in the `postIds` field of the [post endpoint](/docs/apis/post/overview) or the `id` field from the [get all history endpoint](/docs/apis/history/get-history) or the ID from a post URL, such as from this [Facebook post](https://www.facebook.com/Ayrshare/posts/pfbid02ktEFyG8EydxNGyiytsiC4m3X8kQAfTC6inCCzZJZD9KpAZAiWPpSK5C5HS7cgD54l). The linked account must be the owner of the post to retrieve the history. Supported platforms: Facebook, Instagram, LinkedIn, Threads, TikTok, Twitter, and YouTube with the following `platform` values: `facebook`, `instagram`, `linkedin`, `threads`, `tiktok`, `twitter`, `youtube`. ## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` **Profile-Key** `string` [Profile Key](/docs/apis/overview#profile-key-format) of a User Profile. Format: `Profile-Key: PROFILE_KEY` ## Path Parameters **socialId** `string` *required* The [Social Post ID](/docs/apis/overview#id-types) of the post. > [!NOTE] > > Please see the [/post endpoint](/docs/apis/post/post) Response 200 for another example of the Facebook > Social Post ID `104923907983682_108329000309742` ## Query Parameters **searchPlatformId** `boolean` *required* Always equals `true` **platform** `string` *required* Values: `facebook`, `instagram`, `linkedin`, `threads`, `tiktok`, `twitter`, `youtube` ```bash cURL theme={"system"} curl \ -H "Authorization: Bearer API_KEY" \ -X GET https://api.ayrshare.com/api/history/104923907983682_108329000309742?searchPlatformId=true&platform=facebook ``` ```javascript JavaScript theme={"system"} const url = "https://api.ayrshare.com/api/history/104923907983682_108329000309742?searchPlatformId=true&platform=facebook"; const response = await fetch(url, { headers: { 'Authorization': 'Bearer API_KEY' } }); ``` ```python Python theme={"system"} import requests url = 'https://api.ayrshare.com/api/history/104923907983682_108329000309742?searchPlatformId=true&platform=facebook' headers = {'Authorization': 'Bearer API_KEY'} response = requests.get(url, headers=headers) ``` ```php PHP theme={"system"} $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, 'https://api.ayrshare.com/api/history/104923907983682_108329000309742?searchPlatformId=true&platform=facebook'); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET'); curl_setopt($ch, CURLOPT_HTTPHEADER, array('Authorization: Bearer API_KEY')); $response = curl_exec($ch); curl_close($ch); ``` ```csharp C# theme={"system"} using System; using System.Net.Http; using System.Threading.Tasks; namespace HistorySocialIdGETRequest_csharp { class HistorySocialId { private static readonly HttpClient client = new HttpClient(); static async Task Main(string[] args) { string API_KEY = "API_KEY"; string url = "https://api.ayrshare.com/api/history/104923907983682_108329000309742?searchPlatformId=true&platform=facebook"; client.DefaultRequestHeaders.Add("Authorization", "Bearer " + API_KEY); try { var response = await client.GetStringAsync(url); Console.WriteLine(response); } catch (HttpRequestException e) { Console.WriteLine($"Error: {e.Message}"); } } } } ``` ```go Go theme={"system"} client := &http.Client{} req, err := http.NewRequest("GET", "https://api.ayrshare.com/api/history/104923907983682_108329000309742?searchPlatformId=true&platform=facebook", nil) req.Header.Add("Authorization", "Bearer API_KEY") resp, err := client.Do(req) ``` ```ruby Ruby theme={"system"} require 'net/http' require 'uri' uri = URI.parse("https://api.ayrshare.com/api/history/104923907983682_108329000309742?searchPlatformId=true&platform=facebook") request = Net::HTTP::Get.new(uri) request['Authorization'] = 'Bearer API_KEY' response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == 'https') do |http| http.request(request) end ``` ```json 200: Success theme={"system"} [ { "commentsCount": 0, "created": "2024-01-25T23:01:23Z", "from": { "name": "Ayrshare", "id": "18930271191" }, "id": "18930271191_39392923", "mediaUrls": [ { "media": { "image": { "height": 720, "src": "https://scontent-lga3-2.xx.fbcdn.net/v/...", "width": 405 }, "source": "https://video-lga3-2.xx.fbcdn.net/o1/..." }, "title": "🔥 Sneaker time!" } ], "messageTags": [ { "id": "434786466522", "name": "#sneakerswap365", "offset": 242, "length": 15 } ], "post": "🔥 Sneaker Time for all!", "postUrl": "https://www.facebook.com/reel/72681340799/", "lastUpdated": "2024-01-25T23:29:58.014Z", "nextUpdate": "2024-01-25T23:40:58.014Z" } ] ``` ```json 400: Post Not Found theme={"system"} { "action": "get", "status": "error", "code": 221, "message": "The ID was not found. Please verify the id, API or Profile Keys, and required parameters.", "id": "7268134079", "lastUpdated": "2024-01-25T23:45:14.311Z", "nextUpdate": "2024-01-25T23:56:14.311Z" } ``` # History API Overview Source: https://www.ayrshare.com/docs/apis/history/overview Retrieve your Ayrshare post history, or use the platform and social ID history endpoints to fetch posts made directly on the social networks. The Ayrshare history endpoint allows you to retrieve posts from your connected social media accounts, whether they were published through Ayrshare's platform or posted directly on the social networks themselves. This means you can access your social media history through a single endpoint, making it easier to analyze and track all your social content regardless of how it was originally posted. # Create Short Link Source: https://www.ayrshare.com/docs/apis/links/create-short-link POST /links Provide a URL to the /links endpoint and get a shortened link back, with optional UTM parameters such as utmSource and click analytics on browser type. Provide a URL and a shortened link will be returned. Analytics can then be gathered on the clicks, browser type, etc. Submitting the same URL for shortening will always result in the same shortened link. To generate a unique shortened link for the same URL, you can add extra query parameters to it. For example, appending a unique identifier like [https://ayrshare.com?uniqueId=123](https://ayrshare.com?uniqueId=123) will create a distinct shortened link. You may also include UTM parameters, which will be embedded in the shortened URL link. ## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` ## Body Parameters **url** `string` *required* URL to be shortened. Must be a valid URL starting with https\:// **utmId** `string` Used to identify which Google Analytics ads campaign this referral references. **utmSource** `string` Used to identify a search engine, newsletter name, or other source. **utmMedium** `string` Used to identify a medium such as email or cost-per-click. **utmCampaign** `string` Used for keyword analysis. Used to identify a specific product promotion or strategic campaign. **utmTerm** `string` Used for paid search. Used to note the keywords for this ad. **utmContent** `string` Used for A/B testing and content-targeted ads. Used to differentiate ads or links that point to the same URL. ```bash cURL theme={"system"} curl \ -H "Authorization: Bearer API_KEY" \ -H 'Content-Type: application/json' \ -d '{"url": "https://www.ayrshare.com", "utmSource": "google_ads"}' \ -X POST https://api.ayrshare.com/api/links ``` ```javascript JavaScript theme={"system"} const API_KEY = "API_KEY"; fetch("https://api.ayrshare.com/api/links", { method: "POST", headers: { "Content-Type": "application/json", "Authorization": `Bearer ${API_KEY}` }, body: JSON.stringify({ url: "https://www.ayrshare.com", // required utmSource: "google_ads", // optional }), }) .then((res) => res.json()) .then((json) => console.log(json)) .catch(console.error); ``` ```python Python theme={"system"} import requests payload = {'url': 'https://www.ayrshare.com', 'utmSource': 'google_ads"} headers = {'Content-Type': 'application/json', 'Authorization': 'Bearer API_KEY'} r = requests.post('https://api.ayrshare.com/api/links', json=payload, headers=headers) print(r.json()) ``` ```php PHP theme={"system"} require 'vendor/autoload.php';// Composer auto-loader using Guzzle. See .../guzzlephp.org/en/stable/overview.html $client = new GuzzleHttp\Client(); $res = $client->request( 'POST', 'https://api.ayrshare.com/api/links', [ 'headers' => [ 'Content-Type' => 'application/json', 'Authorization' => 'Bearer API_KEY' ], 'json' => [ 'url' => 'https://www.ayrshare.com', 'utmSource' => 'google_ads', // optional ] ] ); echo json_encode(json_decode($res->getBody()), JSON_PRETTY_PRINT); ``` ```go Go theme={"system"} package main import ( "bytes" "encoding/json" "log" "net/http" ) func main() { message := map[string]interface{}{ "url": "https://www.ayrshare.com", "utmSource": "google_ads", } bytesRepresentation, err := json.Marshal(message) if err != nil { log.Fatalln(err) } req, _ := http.NewRequest("POST", "https://api.ayrshare.com/api/links", bytes.NewBuffer(bytesRepresentation)) req.Header.Add("Content-Type", "application/json; charset=UTF-8") req.Header.Add("Authorization", "Bearer API_KEY") res, err := http.DefaultClient.Do(req) if err != nil { log.Fatal("Error:", err) } res.Body.Close() } ``` ```csharp C# theme={"system"} using System; using System.Net.Http; using System.Text; using System.Threading.Tasks; namespace CreateShortLinkPOSTRequest_csharp { class CreateShortLink { private static readonly HttpClient client = new HttpClient(); static async Task Main(string[] args) { string API_KEY = "API_KEY"; string url = "https://api.ayrshare.com/api/analytics/links"; client.DefaultRequestHeaders.Add("Authorization", "Bearer " + API_KEY); string json = "{\"url\" : \"https://www.ayrshare.com\"," + "\"utmSource\" :\"google_ads\""; try { var content = new StringContent(json, Encoding.UTF8, "application/json"); HttpResponseMessage response = await client.PostAsync(url, content); response.EnsureSuccessStatusCode(); string responseBody = await response.Content.ReadAsStringAsync(); Console.WriteLine(responseBody); } catch (HttpRequestException e) { Console.WriteLine($"Error: {e.Message}"); } } } } ``` ```json 200: Success theme={"system"} { "created": "2023-07-17T15:20:51.118Z", "id": "yC0fTl", "originalUrl": "https://www.ayrshare.com/?utm_source=looking", "shortUrl": "https://ayrs.io/yC0fTl", "status": "success", "utmSource": "looking" } ``` ```json 400: Error Shortening Link theme={"system"} { "action": "request", "status": "error", "code": 126, "message": "Shorten URL failed. Please verify the URL is properly formatted and the correct UTM parameters are used." } ``` # Link Analytics Source: https://www.ayrshare.com/docs/apis/links/link-analytics GET /links/:id Return analytics for all shortened links or a single link ID, with optional fromCreatedDate, toCreatedDate, fromClickDate, and toClickDate UTC filters. Return analytics for all shortened links or a single link for a given link ID. For example:
    • `https://api.ayrshare.com/api/links/yC0fTl` returns analytics for ID `yC0fTl`
    • `https://api.ayrshare.com/api/links` returns all link analytics.
    ## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` ## Path Parameters **id** `string` Provide the shortened link ID returned from the POST /links request as a path parameter. For example: `https://api.ayrshare.com/api/links/yC0fTl` If no link ID is provided, all links are returned. ## Query Parameters **fromCreatedDate** `string` Get history of links shortened after this date. Accepts a UTC date time. For example, use format `YYYY-MM-DDThh:mm:ssZ` and send as `2026-07-08T12:30:00Z`. Please see [utctime](https://www.utctime.net/) for more examples. **toCreatedDate** `string` Get history of links shortened before this date. Accepts a UTC date time. For example, use format `YYYY-MM-DDThh:mm:ssZ` and send as `2026-07-08T12:30:00Z`. Please see [utctime](https://www.utctime.net/) for more examples. **fromClickDate** `string` Get history of links clicked after this date. Accepts a UTC date time. For example, use format `YYYY-MM-DDThh:mm:ssZ` and send as `2026-07-08T12:30:00Z`. Please see [utctime](https://www.utctime.net/) for more examples. **toClickDate** `string` Get history of links clicked before this date. Accepts a UTC date time. For example, use format `YYYY-MM-DDThh:mm:ssZ` and send as `2026-07-08T12:30:00Z`. Please see [utctime](https://www.utctime.net/) for more examples. ```bash cURL theme={"system"} curl \ -H "Authorization: Bearer API_Key" \ -H 'Content-Type: application/json' \ -X GET https://api.ayrshare.com/api/links/yC0fTl ``` ```javascript JavaScript theme={"system"} const API_KEY = "API_KEY"; fetch(`https://api.ayrshare.com/api/links/yC0fTl`, { method: "GET", headers: { "Content-Type": "application/json", "Authorization": `Bearer ${API_KEY}` } }) .then((res) => res.json()) .then((json) => console.log(json)) .catch(console.error); ``` ```python Python theme={"system"} import requests headers = {'Content-Type': 'application/json', 'Authorization': 'Bearer API_KEY'} r = requests.delete('https://api.ayrshare.com/api/links/yC0fTl', headers=headers) print(r.json()) ``` ```php PHP theme={"system"} request( 'GET', 'https://api.ayrshare.com/api/links/yC0fTl', [ 'headers' => [ 'Content-Type' => 'application/json', 'Authorization' => 'Bearer API_KEY' ] ] ); echo json_encode(json_decode($res->getBody()), JSON_PRETTY_PRINT); ``` ```csharp C# theme={"system"} using System; using System.Net.Http; using System.Threading.Tasks; namespace LinksGETRequest_csharp { class Links { private static readonly HttpClient client = new HttpClient(); static async Task Main(string[] args) { string API_KEY = "API_KEY"; string url = "https://api.ayrshare.com/api/links/yC0fTl"; client.DefaultRequestHeaders.Add("Authorization", "Bearer " + API_KEY); try { var response = await client.GetStringAsync(url); Console.WriteLine(response); } catch (HttpRequestException e) { Console.WriteLine($"Error: {e.Message}"); } } } } ``` ```json 200: Analytics on a Link theme={"system"} { "status": "success", "analytics": { "browserCounts": { "chrome": 15 }, "created": "2023-06-29T00:57:12.220Z", "id": "yC0fTl", "originalUrl": "https://www.ayrshare.com/?utm_source=google_ads", "refererCounts": {}, "shortUrl": "https://ayrs.io/yC0fTl", "socialClicks": {}, "status": "success", "totalClicks": 15, "utmSource": "google_ads" } } ``` ```json 200: Analytics on all Links theme={"system"} { "status": "success", "analytics": [ { "browserCounts": { "chrome": 3 }, "created": "2023-04-07T22:33:41.126Z", "id": "pHXlbv9JlRxrXj6rstdMu", "originalUrl": "https://www.ayrshare.com/", "refererCounts": {}, "shortUrl": "https://ayrs.io/pHXlbv9JlRxrXj6rstdMu", "socialClicks": {}, "totalClicks": 3 }, { "browserCounts": { "edge": 13 }, "created": "2023-07-17T15:20:51.118Z", "id": "yC0fTl", "originalUrl": "https://www.ayrshare.com/?utm_source=looking", "refererCounts": {}, "shortUrl": "https://ayrs.io/yC0fTl", "socialClicks": {}, "totalClicks": 13, "utmSource": "looking" } ] } ``` ```json 400: Bad Request theme={"system"} { "status": "error", "message": "Unable to find link ID: yC0fTl", "code": 187 } ``` # Links API Overview Source: https://www.ayrshare.com/docs/apis/links/overview Shorten links to include in social posts with the /links endpoint, track link performance, and use a custom link domain on Business and Enterprise plans. With the /links endpoint you can shorten links to include in social posts. The Ayrshare link shortener offers several valuable benefits for social publishing.
    • Condenses long and complex URLs into more visually appealing, concise, and memorable links.
    • Analytics and tracking capabilities, allowing you to monitor link performance, engagement, and audience insights.
    • Save valuable character space, especially on platforms like Twitter where character limits are stringent.
    • Add a custom link domain with your own url.
    ## Custom Link Domain A custom link domain allows you to personalize and brand the domain name used for link shortening and redirection. Instead of using a generic link shortener domain (e.g., bit.ly, ayr.app), a custom link domain allows you to use own domain name to create shortened links. Please contact us to set up your custom domain. Available on Business and Enterprise plans. # Update Short Link Source: https://www.ayrshare.com/docs/apis/links/update-short-link PUT /links/:id Update the destination URL of a short link by the ID returned from POST /links, and see how social media preview caching affects Open Graph preview cards. Update the destination URL of an existing short link. The short link ID is returned when you first create a short link via `POST /links`. > [!NOTE] > > **Social Media Preview Caching:** Social media platforms cache Open Graph preview cards from the original destination URL. After updating a short link, the preview card shown on social platforms may not match the new destination until the cache expires. ## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` ## Path Parameters **id** `string` *required* The short link ID returned from `POST /links` (e.g., "TkuWEy"). ## Body Parameters **url** `string` *required* The new destination URL. Must be a valid URL starting with https\:// ```bash cURL theme={"system"} curl \ -H "Authorization: Bearer API_KEY" \ -H 'Content-Type: application/json' \ -d '{"url": "https://www.new-destination.com"}' \ -X PUT https://api.ayrshare.com/api/links/TkuWEy ``` ```javascript JavaScript theme={"system"} const API_KEY = "API_KEY"; fetch("https://api.ayrshare.com/api/links/TkuWEy", { method: "PUT", headers: { "Content-Type": "application/json", "Authorization": `Bearer ${API_KEY}` }, body: JSON.stringify({ url: "https://www.new-destination.com", // required }), }) .then((res) => res.json()) .then((json) => console.log(json)) .catch(console.error); ``` ```python Python theme={"system"} import requests payload = {'url': 'https://www.new-destination.com'} headers = {'Content-Type': 'application/json', 'Authorization': 'Bearer API_KEY'} r = requests.put('https://api.ayrshare.com/api/links/TkuWEy', json=payload, headers=headers) print(r.json()) ``` ```php PHP theme={"system"} require 'vendor/autoload.php';// Composer auto-loader using Guzzle. See .../guzzlephp.org/en/stable/overview.html $client = new GuzzleHttp\Client(); $res = $client->request( 'PUT', 'https://api.ayrshare.com/api/links/TkuWEy', [ 'headers' => [ 'Content-Type' => 'application/json', 'Authorization' => 'Bearer API_KEY' ], 'json' => [ 'url' => 'https://www.new-destination.com', ] ] ); echo json_encode(json_decode($res->getBody()), JSON_PRETTY_PRINT); ``` ```json 200: Success theme={"system"} { "status": "success", "id": "TkuWEy", "originalUrl": "https://www.new-destination.com", "shortUrl": "https://ayrs.io/TkuWEy", "created": "2023-07-17T15:20:51.118Z", "updated": "2024-01-10T12:30:00.000Z" } ``` ```json 400: Invalid URL Format theme={"system"} { "action": "request", "status": "error", "code": 126, "message": "Shorten URL failed. Please verify the URL is properly formatted and the correct UTM parameters are used." } ``` ```json 404: Link Not Found theme={"system"} { "action": "link", "status": "error", "code": 418, "message": "Link not found or not owned by user." } ``` # Get Brand Data Source: https://www.ayrshare.com/docs/apis/listen/brand-user GET /brand/byUser Retrieve public account details from Bluesky, Facebook Pages, Instagram Business or Creator accounts, LinkedIn, X, or YouTube, even if not linked to Ayrshare. This endpoint allows you to retrieve information about any public social media profile, even if that profile is not linked to your Ayrshare account. This feature is available for Bluesky, Facebook, Instagram, LinkedIn, X, and YouTube. For profiles that are linked to your Ayrshare account, we recommend using the [/analytics](/docs/apis/analytics/social) endpoint instead, as it provides more detailed analytics. Important: To search for a profile on a specific social platform, your Ayrshare account must have that social network linked. For example, to search for any Instagram profile, e.g. `@taylorswift`, you must have Instagram linked to your Ayrshare account. > [!WARNING] > > The searched account must be public to access the data. Private social accounts are not available. > > Location data is only available for business with public locations. > Tagging a location in Facebook or Instagram requires an available location. ## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` **Profile-Key** `string` [Profile Key](/docs/apis/overview#profile-key-format) of a User Profile. Format: `Profile-Key: PROFILE_KEY` ## Query Parameters **platforms** `array` *required* String array of platforms: `bluesky` ,`instagram`, `facebook`, `linkedin`, `twitter`, or `youtube`. **blueskyUser** `string` Bluesky handle URL encoded. For example: "@ayrshare" or "ayrshare". Required if "bluesky" in `platforms` array. **facebookUser** `string` Facebook Page name URL encoded. For example "@newyorkgiants" or "newyorkgiants". Facebook personal accounts are not permitted by Facebook. Required if "facebook" in `platforms` array. **instagramUser** `string` Instagram handle URL encoded. For example: "@nygiants" or "nygiants". Note: Only Instagram Business and Creator accounts can be returned. Required if "instagram" in `platforms` array. **linkedinUser** `string` LinkedIn company (organization) vanity name such as `Linkedin` or `linkedin-marketing-solutions`. You can also look up a person using their LinkedIn person ID retrieved from the history endpoint, i.e. people who liked a post. An example ID: `urn:li:person:Z_yXaxh_Et` Required if "linkedin" in `platforms` array. **twitterUser** `string` Twitter handle URL encoded. For example: "@ayrshare" or "ayrshare". Required if "twitter" in `platforms` array. **youtubeUser** `string` YouTube username URL encoded, channel ID, or playlist ID. Note, channel IDs typically begin with "UC" and playlist IDs begin with "PL". For example, send the username "MelissaEtheridgeVEVO" or the channel ID as "UCpSUQewzOXg1F0zLmieKCqQ". You may also use the YouTube handle found in the YouTube URL. For example, the handle for [https://www.youtube.com/@mkbhd](https://www.youtube.com/@mkbhd) is "@mkbhd". Be sure to keep the `@` symbol to designate the handle. Required if "youtube" in `platforms` array. ```bash cURL theme={"system"} curl \ -H "Authorization: Bearer API_KEY" \ -X GET https://api.ayrshare.com/api/brand/byUser?platforms[0]=instagram&platforms[1]=twitter&twitterUser=ayrshare&instagramUser=nygiants ``` ```javascript JavaScript theme={"system"} const API_KEY = "API_KEY"; fetch( "https://api.ayrshare.com/api/brand/byUser?platforms[0]=instagram&platforms[1]=twitter&twitterUser=ayrshare&instagramUser=nygiants", { method: "GET", headers: { Authorization: `Bearer ${API_KEY}` } } ) .then((res) => res.json()) .then((json) => console.log(json)) .catch(console.error); ``` ```python Python theme={"system"} import requests headers = {'Authorization': 'Bearer API_KEY'} r = requests.get('https://api.ayrshare.com/api/brand/byUser?platforms[0]=instagram&platforms[1]=twitter&twitterUser=ayrshare&instagramUser=nygiants', headers=headers) print(r.json()) ``` ```php PHP theme={"system"} $apiUrl = 'https://api.ayrshare.com/api/brand/byUser?platforms[0]=instagram&platforms[1]=twitter&twitterUser=ayrshare&instagramUser=nygiants'; $apiKey = 'API_KEY'; // Replace 'API_KEY' with your actual API key $headers = [ 'Content-Type: application/json', 'Authorization: Bearer ' . $apiKey, ]; $curl = curl_init($apiUrl); curl_setopt_array($curl, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => $headers ]); $response = curl_exec($curl); if ($response === false) { echo 'Curl error: ' . curl_error($curl); } else { echo json_encode(json_decode($response), JSON_PRETTY_PRINT); } curl_close($curl); ``` ```csharp C# theme={"system"} using System; using System.Net.Http; using System.Threading.Tasks; namespace BrandByUserGETRequest_csharp { class BrandByUser { static async Task Main(string[] args) { string API_KEY = "API_KEY"; string url = "https://api.ayrshare.com/api/brand/byUser?platforms[0]=instagram&platforms[1]=twitter&twitterUser=ayrshare&instagramUser=nygiants"; using (var client = new HttpClient()) { client.DefaultRequestHeaders.Add("Authorization", "Bearer " + API_KEY); try { var response = await client.GetStringAsync(url); Console.WriteLine(response); } catch (HttpRequestException ex) { Console.WriteLine($"Error: {ex.Message}"); } } } } } ``` ```json 200: Success theme={"system"} { "bluesky": { "avatar": "https://cdn.bsky.app/img/avatar/plain/did:plc:62musrcyanhro2lydyhlw7ci/bafkreiegtpqhpwgu6tww2ejsdil4ew3blmt6jh3wlq6zhanxo3wj2ib4du@jpeg", "description": "Ayrshare's Social APIs provide the core infrastructure for social media posting, management, and analytics.", "displayName": "Ayrshare", "handle": "ayrshare.com", "id": "did:plc:62musrcyanhro2lydyhlw7ci", "indexedAt": "2024-11-29T21:13:18.046Z" }, "facebook": { "about": "We invite you to wonder. ", "description": "Frank Lloyd Wright's architectural masterpiece home to a world-renowned collection of modern and contemporary art.", "fanCount": 845048, "followersCount": 874337, "id": "7640348500", "isUnclaimed": false, // whether a Facebook Page that was automatically generated has been claimed by the business it represents. "link": "https://www.facebook.com/7640348500", "location": { "city": "New York", "country": "United States", "latitude": 40.782910059774, "longitude": -73.959075808525, "state": "NY", "street": "1071 5th Ave", "zip": "10128" }, "name": "Solomon R. Guggenheim Museum", "picture": { "data": { "height": 50, "isSilhouette": false, "url": "https://scontent.ford4-1.fna.fbcdn.net/v/t39.30808-1/352122020_2000703126929355_7618417261219676343_n.jpg?stp=cp0_dst-jpg_p50x50&_nc_cat=104&ccb=1-7&_nc_sid=4da83f&_nc_ohc=Y1AlQn-HyvwAX8NzExq&_nc_ht=scontent.ford4-1.fna&edm=AJdBtusEAAAA&oh=00_AfD7ms2Nv0b5x1jr_uJKZsXnngQP3dmDrjQDNz_4aaBrCg&oe=65D3ACAE", "width": 50 } }, "username": "guggenheimmuseum", "verificationStatus": "blue_verified", // The verification status of the Facebook Page that represents a business, blue_verified or not_verified. "website": "http://www.guggenheim.org/" }, "instagram": { "biography": "4x Super Bowl Champions #TogetherBlue", "followersCount": 2278171, "followsCount": 228, "id": "17841400118294090", // Instagram Id "igId": 261763943, "mediaCount": 8965, "name": "New York Giants", "profilePictureUrl": "https://scontent-lga3-2.xx.fbcdn.net/v/t51.2885-15/209249968_563247608171409_1254577321735891919_n.jpg?_nc_cat=1&ccb=1-5&_nc_sid=86c713&_nc_ohc=T673IyEiuasAX_Jj7xu&_nc_ht=scontent-lga3-2.xx&edm=AL-3X8kEAAAA&oh=00_AT8GNdOos4riN7NhrI06a6TkVKgOf5p_RUlsOQUwPRW3VQ&oe=6247F2F0", "username": "nygiants", "website": "http://nygnt.co/vgle2" }, // LinkedIn using the username or handle "linkedin": { "localizedName": "LinkedIn", "name": { "localized": { "it_IT": "LinkedIn", "ru_RU": "LinkedIn", "pl_PL": "LinkedIn", "ro_RO": "LinkedIn", "sv_SE": "LinkedIn" }, "preferredLocale": { "country": "US", "language": "en" } }, "id": 1337, "vanityName": "linkedin", "organizationType": "PUBLIC_COMPANY", "locations": [ { "locationType": "HEADQUARTERS", "address": { "geographicArea": "CA", "country": "US", "city": "Sunnyvale", "line1": "1000 W Maude", "postalCode": "94085" }, "streetAddressFieldState": "UNSET_OPT_OUT", "geoLocation": "urn:li:geo:106316449", "staffCountRange": "SIZE_1" }, { "locationType": "OTHER", "address": { "geographicArea": "Community of Madrid", "country": "ES", "city": "Madrid", "postalCode": "28046" }, "streetAddressFieldState": "OPT_OUT", "geoLocation": "urn:li:geo:106809575", "staffCountRange": "SIZE_1" }, { "locationType": "OTHER", "address": { "geographicArea": "ON", "country": "CA", "city": "Toronto", "postalCode": "M5J 2Z2" }, "streetAddressFieldState": "OPT_OUT", "geoLocation": "urn:li:geo:108528311", "staffCountRange": "SIZE_1" } ], "specialties": [ { "locale": { "country": "US", "language": "en" }, "tags": [ "Online Professional Network", "Jobs", "People Search", "Company Search", "Address Book", "Advertising", "Professional Identity", "Group Collaboration", "Recruiting" ] } ], "website": "https://careers.linkedin.com", "description": "Founded in 2003, LinkedIn connects the world's professionals to make them more productive and successful. With more than 1 billion members worldwide, including executives from every Fortune 500 company, LinkedIn is the world's largest professional network. The company has a diversified business model with revenue coming from Talent Solutions, Marketing Solutions, Sales Solutions and Premium Subscriptions products. Headquartered in Silicon Valley, LinkedIn has offices across the globe..", "media": { "mediaUrl": "https://media.licdn.com/dms/image/C560BAQHaVYd13rRz3A/company-logo_400_400/0/1638831590218/linkedin_logo?e=1723680000&v=beta&t=gOk8XZWklJyh3O7qcgWRluAgbt8whoV8Kr9B0E74xYI", "id": "urn:li:digitalmediaAsset:C560BAQHaVYd13rRz3A", "mediaExpiresSeconds": 1723680000000 }, "lastUpdated": "2024-05-15T20:42:09.417Z", "nextUpdate": "2024-05-15T20:53:09.417Z" }, // LinkedIn using a person ID. "linkedin": { "from": { "name": "John Doe", "id": "Z_yXaxh", "url": "https://www.linkedin.com/in/johndoe", "description": "Founder" }, "media": { "id": "urn:li:image:C5103AQHORT70jVfKVA", "mediaExpiresSeconds": 1728518400000, "url": "https://media.licdn.com/dms/image/v2/C5103AQHORT70jVfKVA/profile" }, "platform": "linkedin", "profileImageUrl": "https://media.licdn.com/dms/image/v2/C5103AQHORT70jVfKVA/profile", "userName": "johndoe" }, "twitter": { "createdAt": "2017-10-04T15:26:17.000Z", "description": "Ayrshare's APIs provide the core infrastructure for social media posting, management, and analytics. https://t.co/UlcRGcg1X9", "id": "92839209304423", "location": "New York, NY", "name": "Ayrshare", "profileImageUrl": "https://pbs.twimg.com/profile_images/1423334467389767680/ochnivwr_normal.jpg", "publicMetrics": { "followersCount": 5361, "followingCount": 39, "tweetCount": 844, "listedCount": 5 }, "url": "https://t.co/UlcRGcg1X9", "username": "Ayrshare" }, "youtube": { "created": "2009-05-12T05:28:43Z", "description": "", "hiddenSubscriberCount": false, "isLinked": true, "longUploadsStatus": "longUploadsUnspecified", "madeForKids": false, "playlistId": "UU-0dv2mN6SeXwkEKtfKXJnQ", "privacyStatus": "public", "subscriberCount": "28200", "thumbnailUrl": "https://yt3.ggpht.com/OwargtIRcXzB7jlHWgCmitNo-6JX2wbZGdOMg5K7rd5BnX4bSJX1WPaD2Bi4RN3X9SJi4D4h4g=s88-c-k-c0x00ffffff-no-nd-rj", "title": "MelissaEtheridgeVEVO", "url": "https://www.youtube.com/c/@MelissaEtheridgeVEVO", "videoCount": "43", "viewCount": "29872467" } } ``` ```json 400: Bad Request Error theme={"system"} { "status": "error", "code": 187, "instagram": { "action": "analytics", "status": "error", "code": 294, "message": "Error getting analytics. The user with username: whoblhablah cannot be found.", "platform": "instagram" }, "twitter": { "action": "analytics", "status": "error", "code": 294, "message": "Error getting analytics. Could not find user with username: [NoDonkdfjkd]." }, "linkedin": { "action": "analytics", "status": "error", "code": 294, "message": "Error getting analytics.", "details": "No LinkedIn organization found for linkedinfdfd" } } ``` # Search Tweets by Keyword Source: https://www.ayrshare.com/docs/apis/listen/keyword/search-tweets GET /listen/keyword Search X/Twitter for tweets matching keywords, hashtags, and search operators, returning user info and engagement metrics. Requires BYOK for X/Twitter. Search X/Twitter for tweets matching keywords, hashtags, and advanced search operators. Returns normalized tweet data including user info, engagement metrics, and entities. This endpoint requires [Bring Your Own Keys (BYOK)](/docs/dashboard/connect-social-accounts/x-twitter-byo-keys) for X/Twitter. > [!WARNING] > > **Important Limitations** > > * Only tweets from the last \~7 days are available (X API limitation). > * Default daily limit of 25 calls. > * BYOK keys for X/Twitter are required. ## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` **Profile-Key** `string` [Profile Key](/docs/apis/overview#profile-key-format) of a User Profile. Format: `Profile-Key: PROFILE_KEY` ## Query Parameters **query** `string` *required* Keyword search query. Supports X/Twitter search operators (see table below). Examples: `ayrshare`, `#socialmedia`, `ayrshare OR #socialmedia`, `from:ayrshare`. **platform** `string` *required* Must be `twitter`. **limit** `integer` (default: 15) Maximum number of tweets to return. Must be between 10 and 100. **sinceId** `string` Returns tweets with an ID greater than (newer than) this value. Useful for fetching only new tweets since a previous request. **untilId** `string` Returns tweets with an ID less than (older than) this value. Useful for paginating backwards through results. **next** `string` Pagination token from a previous response's `meta.pagination.next`. Use this to fetch the next page of results. ## Search Operators The `query` parameter supports the following X/Twitter search operators: | Operator | Description | Example | | - | - | - | | `AND` | Both terms must appear (default behavior) | `social AND media` | | `OR` | Either term must appear | `ayrshare OR socialmedia` | | `from:` | Tweets from a specific user | `from:ayrshare` | | `to:` | Tweets directed at a specific user | `to:ayrshare` | | `#` | Match a hashtag | `#socialmedia` | | `@` | Match a mention | `@ayrshare` | | `-` | Exclude a term | `social -spam` | | `lang:` | Filter by language | `ayrshare lang:en` | | `filter:` | Filter by content type | `ayrshare filter:links` | | `url:` | Match a URL | `url:ayrshare.com` | ```bash cURL theme={"system"} curl \ -H "Authorization: Bearer API_KEY" \ -H "x-twitter-oauth1-api-key: YOUR_TWITTER_API_KEY" \ -H "x-twitter-oauth1-api-secret: YOUR_TWITTER_API_SECRET" \ -X GET "https://api.ayrshare.com/api/listen/keyword?query=ayrshare%20OR%20%23socialmedia&platform=twitter&limit=15" ``` ```javascript JavaScript theme={"system"} const API_KEY = "API_KEY"; fetch( "https://api.ayrshare.com/api/listen/keyword?query=ayrshare%20OR%20%23socialmedia&platform=twitter&limit=15", { method: "GET", headers: { Authorization: `Bearer ${API_KEY}`, "x-twitter-oauth1-api-key": "YOUR_TWITTER_API_KEY", "x-twitter-oauth1-api-secret": "YOUR_TWITTER_API_SECRET" } } ) .then((res) => res.json()) .then((json) => console.log(json)) .catch(console.error); ``` ```python Python theme={"system"} import requests headers = { 'Authorization': 'Bearer API_KEY', 'x-twitter-oauth1-api-key': 'YOUR_TWITTER_API_KEY', 'x-twitter-oauth1-api-secret': 'YOUR_TWITTER_API_SECRET' } params = { 'query': 'ayrshare OR #socialmedia', 'platform': 'twitter', 'limit': 15 } r = requests.get('https://api.ayrshare.com/api/listen/keyword', headers=headers, params=params) print(r.json()) ``` ```json 200: Success theme={"system"} { "status": "success", "platform": "twitter", "query": "ayrshare OR #socialmedia", "tweets": [ { "id": "1234567890", "text": "Just discovered @ayrshare for managing social media APIs!", "createdAt": "2026-03-22T14:30:00.000Z", "user": { "id": "987654321", "name": "Jane Doe", "screenName": "janedoe", "profileImageUrl": "https://pbs.twimg.com/profile_images/..." }, "metrics": { "retweetCount": 5, "favoriteCount": 12 }, "entities": { "hashtags": ["socialmedia"], "mentions": ["ayrshare"], "urls": [] }, "inReplyToStatusId": null, "isRetweet": false, "lang": "en" } ], "meta": { "pagination": { "hasMore": true, "next": "b26v89c19zqg8o3fpds7h...", "limit": 15 } } } ``` ```json 400: Missing Query theme={"system"} { "action": "listen", "status": "error", "code": 428, "message": "Missing required `query` parameter for keyword search" } ``` ```json 400: Unsupported Platform theme={"system"} { "action": "listen", "status": "error", "code": 429, "message": "Missing or unsupported `platform` parameter. Currently only `twitter` is supported" } ``` ```json 403: Plan Required theme={"system"} { "action": "listen", "status": "error", "code": 430, "message": "Keyword search requires Premium, Launch, Business, or Enterprise tier" } ``` ```json 403: BYOK Keys Required theme={"system"} { "action": "listen", "status": "error", "code": 431, "message": "Keyword search requires BYOK (Bring Your Own Keys) for X/Twitter" } ``` ```json 429: Daily Limit Reached theme={"system"} { "action": "listen", "status": "error", "code": 432, "message": "Daily keyword search limit exceeded. Limit resets at midnight UTC" } ``` # Listen API: Social Media Monitoring & Mentions Source: https://www.ayrshare.com/docs/apis/listen/overview The Ayrshare Listen API monitors social media for keywords, hashtags, and mentions in real time to track brand conversations, sentiment, and trends. Look up users' or companies' social media public information, such as followers, profile image, and websites for competitive analysis. Although you still need to have a linked social account, these users and companies that you look up do not need to be a linked Ayrshare user. Also search for Facebook Page and LinkedIn Member Profiles, often used for typeahead mention completion. [Keyword search](/docs/apis/listen/keyword/search-tweets) on X/Twitter is also available on the Premium plan. If your users have linked their social accounts, use the [/analytics](/docs/apis/analytics/social) endpoint for more detailed information. # Search Facebook Pages Source: https://www.ayrshare.com/docs/apis/listen/search/fb-page-search GET /brand/search/facebook Search for Facebook Pages with a search query, often used for typeahead mention completion. Returns the id, link, name, and public location of each Page. Search for Facebook Pages based on a search query. Often used for typeahead mention completion. The response is an array of objects with the id, link, name, and location of the Page. The `location` field will only be returned for Pages with public locations. ## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` **Profile-Key** `string` [Profile Key](/docs/apis/overview#profile-key-format) of a User Profile. Format: `Profile-Key: PROFILE_KEY` ## Query Parameters **search** `string` *required* Search query to find Facebook pages by name. ```bash cURL theme={"system"} curl \ -H "Authorization: Bearer API_KEY" \ -X GET https://api.ayrshare.com/api/brand/search/facebook?search=facebook ``` ```javascript JavaScript theme={"system"} const API_KEY = "API_KEY"; fetch("https://api.ayrshare.com/api/brand/search/facebook?search=facebook", { method: "GET", headers: { "Authorization": `Bearer ${API_KEY}` } }) .then((res) => res.json()) .then((json) => console.log(json)) .catch(console.error); ``` ```python Python theme={"system"} import requests headers = {'Authorization': 'Bearer API_KEY'} r = requests.get('https://api.ayrshare.com/api/brand/search/facebook?search=facebook', headers=headers) print(r.json()) ``` ```json 200: Success theme={"system"} { "facebook": [ { "id": "7640348500", "isUnclaimed": false, "link": "https://www.facebook.com/7640348500", "location": { "city": "New York", "country": "United States", "latitude": 40.782910059774, "longitude": -73.959075808525, "state": "NY", "street": "1071 5th Ave", "zip": "10128" }, "name": "Solomon R. Guggenheim Museum", "verificationStatus": "blue_verified" } ] } ``` # Search LinkedIn Source: https://www.ayrshare.com/docs/apis/listen/search/linkedin-search GET /brand/search/linkedin Search LinkedIn companies by exact vanity name, or people who follow your company page by name, for typeahead @mentions. Needs a linked LinkedIn company page. Search for LinkedIn companies or people based on a search query. This endpoint is commonly used for typeahead mention completion in social media posts. **The linked account must be a LinkedIn company page to search.** Personal LinkedIn accounts can not be used to perform searches. 1. **For mentions in posts**: When implementing @mentions, use this endpoint with typeahead functionality. 2. **Company search**: The exact company vanity name is required. 3. **Person search**: Start with at least 3 characters of a name for best results. 4. **Rate limiting**: This endpoint follows standard API rate limits. > [!NOTE] > > **Search Limitations** > >
      >
    • **Companies**: You can search for any LinkedIn company page
    • > >
    • > **People**: You can only search for people who are followers of your LinkedIn account and the > Ayrshare linked account must be a LinkedIn company page to perform the search. If a person has > their LinkedIn visibility set to private, they will not be found in the search results. >
    • >
    ## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` **Profile-Key** `string` [Profile Key](/docs/apis/overview#profile-key-format) of a User Profile. Format: `Profile-Key: PROFILE_KEY` ## Query Parameters **search** `string` *required* Search query to find LinkedIn companies or people. **Requirements:**
    • Minimum length: 3 characters for people and 1 character for companies.
    • Maximum length: 100 characters for both people and companies.
    **Search behavior:**
    • **For companies**: Use the company's vanity name (found in the LinkedIn URL). Only **exact vanity name matches** will be returned.
      • Example: For `linkedin.com/company/ayrshare`, search for "ayrshare".
      • Searching for partial names like "ayrsh" will NOT return results.
    • **For people**: Use first name and/or last name. Partial name matches are supported.
      • Example: "John Smith" will find people named John Smith. Be sure to URL encode the space.
      • Partial matches like "Joh" or "Smi" will also return results.
      • Remember: Only your LinkedIn followers can be found and the Ayrshare linked account must be a LinkedIn company page to perform the search
    **personOnly** `boolean` (default: false) Controls the search scope: - `false` (default): Searches for companies first, then people if no companies are found. - `true`: Searches only for people (skips company search entirely). ## Examples ```bash Company Search theme={"system"} curl \ -H "Authorization: Bearer API_KEY" \ -X GET "https://api.ayrshare.com/api/brand/search/linkedin?search=ayrshare" ``` ```bash Person Search Only theme={"system"} curl \ -H "Authorization: Bearer API_KEY" \ -X GET "https://api.ayrshare.com/api/brand/search/linkedin?search=John%20Smith&personOnly=true" ``` ```javascript JavaScript theme={"system"} const API_KEY = "API_KEY"; // Search for company or person fetch("https://api.ayrshare.com/api/brand/search/linkedin?search=ayrshare", { method: "GET", headers: { Authorization: `Bearer ${API_KEY}` } }) .then((res) => res.json()) .then((json) => console.log(json)) .catch(console.error); // Search for person only fetch("https://api.ayrshare.com/api/brand/search/linkedin?search=John%20Smith&personOnly=true", { method: "GET", headers: { Authorization: `Bearer ${API_KEY}` } }) .then((res) => res.json()) .then((json) => console.log(json)) .catch(console.error); ``` ```python Python theme={"system"} import requests headers = {'Authorization': 'Bearer API_KEY'} # Search for company or person r = requests.get('https://api.ayrshare.com/api/brand/search/linkedin?search=ayrshare', headers=headers) print(r.json()) # Search for person only r = requests.get('https://api.ayrshare.com/api/brand/search/linkedin?search=john&personOnly=true', headers=headers) print(r.json()) ``` ```json Company Search Result theme={"system"} { "linkedin": [ { "vanityName": "ayrshare", "website": "https://www.ayrshare.com", "groups": [], "description": "Ayrshare's APIs provide the core infrastructure for social media posting, management, and analytics.\n\nThe Ayrshare API takes care of the social media infrastructure so you don't have to. Your team can focus on building your product instead of stitching together and maintaining multiple social media platforms.\n\nPost to Facebook, Twitter, Instagram, LinkedIn, Reddit, Telegram, TikTok, Google My Business, and YouTube.\n", "defaultLocale": { "country": "US", "language": "en" }, "organizationType": "PARTNERSHIP", "alternativeNames": [], "specialties": [ "social media", "api", "saas", "social networks", "tiktok", "facebook", "instagram" ], "staffCountRange": "SIZE_10_TO_100", "name": "Ayrshare", "primaryOrganizationType": "NONE", "locations": [ { "description": { "localized": { "en_US": "Headquarters" }, "preferredLocale": { "country": "US", "language": "en" } }, "locationType": "HEADQUARTERS", "address": { "geographicArea": "New York", "country": "US", "city": "New York", "line1": "142 W 57th St", "postalCode": "10019" }, "localizedDescription": "Headquarters", "streetAddressFieldState": "UNSET_OPT_OUT", "geoLocation": "urn:li:geo:103963738" } ], "id": 66755333 } ] } ``` ```json Person Search Result theme={"system"} { "linkedin": [ { "lastName": "Smith", "firstName": "John", "headline": "CTO at Ayrshare", "id": "urn:li:person:WBwF1C23L" } ] } ``` ```json 400: Search query too short theme={"system"} { "linkedin": { "action": "request", "status": "error", "code": 101, "message": "Missing or incorrect parameters. Please verify with the docs. https://www.ayrshare.com/docs/apis", "details": "The search must be a string between 3 and 100 characters." } } ``` ```json 404: No results found theme={"system"} { "linkedin": [] } ``` # Ayrshare API Overview Source: https://www.ayrshare.com/docs/apis/overview Powerful Social APIs that enable you to send social media posts and get analytics effortlessly. For developers and businesses of all sizes. The Social Media REST API provides developers with programmatic access to multiple social networks through a single unified interface. Through Ayrshare's social API, you can manage social media activities including creating and deleting posts, retrieving analytics, engaging with comments and reviews, managing direct messages, creating Facebook ads, and performing other social media actions across platforms. The API currently supports 14 major social networks: Bluesky, Facebook, Google Business Profile, Instagram, LinkedIn, Pinterest, Reddit, Snapchat, Telegram, Threads, TikTok, WhatsApp, X (formerly Twitter), and YouTube. By integrating with this API, developers can automate social media management tasks across all these platforms simultaneously. API calls request and response data are in JSON format, which allows for easy parsing and processing in most programming languages. The same API is also available over [GraphQL](/docs/apis/graphql/overview), which lets you request several things in one call and gives you a typed, self-describing schema you can explore in the [GraphQL Explorer](https://app.ayrshare.com/graphql-explorer). REST remains the primary interface; both are supported side by side and can be mixed in the same integration. If you are on the Launch, Business, or Enterprise Plan, see the [Business Plan Overview](/docs/multiple-users/business-plan-overview), [Launch Plan Overview](/docs/multiple-users/business-launch-overview), and [/profiles API endpoint](/docs/apis/profiles/overview). ## Key Functionality
    • [14 social networks supported](/docs/introduction#which-social-networks-are-supported).
    • Secure API access using your unique API Key.
    • Scheduled posting to connected social media platforms.
    • Automated posting based on predefined schedules.
    • Support for image and video content, including Reels, Stories, and Spotlight.
    • Delete posts on linked social networks.
    • Comprehensive post engagement analytics (likes, shares, etc.).
    • Social account metrics, including follower count and demographic data.
    • Comment management: view, add, and delete post comments.
    • Optional link shortening for all or specific URLs in posts.
    • Unsplash integration: add specific images or randomly select based on keywords.
    • Automatic hashtag generation option using relevant keywords.
    • Post history tracking, including non-Ayrshare posts.
    • Review management: retrieve, reply to, and delete review responses.
    • RSS feed integration for automated content posting.
    • Media library: upload and store photos and videos for use in posts.
    • [Social Post Verification System](/docs/testing/post-verification) to keep your social accounts safe.
    ## Business Plan and Launch Plan [Business Plan](/docs/multiple-users/business-plan-overview) and [Launch Plan](/docs/multiple-users/business-launch-overview) features for managing multiple users and clients:
    • Enable users to link their own social media accounts to your platform.
    • Secure single sign-on using OAuth for quick account linking.
    • Create and remove user profiles programmatically through the API.
    • Access advanced user analytics.
    • Webhook support for real-time updates.
    • Direct message management across supported platforms.
    • Create Facebook ads from existing posts.
    Contact us to learn more about the [Business Plan](https://www.ayrshare.com/business-plan-for-multiple-users/). ## Ads API The [Ads API](/docs/apis/ads/overview) allows you to create Facebook ads from existing posts.
    • Boost posts to reach more people.
    • Manage ads and track performance.
    • Analyze ad spend and optimize campaigns.
    > [card] [Ads API](/docs/apis/ads/overview) > > Explore the Ads API ## Messages API A unified [Messaging API](/docs/apis/messages/overview) to engage users in conversations across the major social media channels: Facebook, Instagram, and X.
    • Sending text, image, and video messages.
    • Retrieving complete conversation histories.
    • Setting up automated message responses.
    • Receiving real-time updates via webhooks for messages received, message reactions, read receipts.
    This API simplifies user engagement by centralizing messaging operations for multiple social media channels. Learn more... > [card] [Messages API](/docs/apis/messages/overview) > > Explore the unified messaging API ## Max Pack Get even more capabilities with the [Max Pack add-on](/docs/additional/maxpack): > [card] [Max Pack](/docs/additional/maxpack) > > Unlock advanced features like AI-powered content generation, enhanced analytics, and expanded > platform support with our Max Pack add-on. ## Watch How to Use the Social API If you're building in Node.js, check out this video on how to connect and publish posts to X and Facebook. [How to Use Ayrshare's Social API](https://www.youtube.com/embed/OMtj2h6sW6U) ## Social API Demo If you use Node.js see the social API demo code to get started building your own social API integration. > [card] [Social API Demo](https://github.com/ayrshare/social-api-demo) > > Explore our Node.js demo code to jumpstart your social API integration ## Base URL The base URL for the Ayrshare API is the same for all endpoints. `https://api.ayrshare.com/api` ## Authorization Ayrshare authenticates API requests via an Authorization token passed in the HTTP header. Please be sure to send `Bearer` with the API Key. The API Key can be found in the Ayrshare Dashboard by switching to your Primary Profile. If you are a Business or Enterprise user, you can create User Profiles with Profile Keys to manage multiple clients. The Profile Key is used in the header of your requests. The API Key must also be used in the header of your requests for User Profiles. > [!TIP] > > Premium plans should only use the API Key in the header of their requests. Business and Enterprise > plans should use the Profile Key in the header of their requests when interacting on behalf of a > User Profile. ### API Key Format `Authorization: Bearer API_KEY` replacing API\_KEY with your Primary Profile API Key, which can be [found in the Ayrshare Dashboard](/docs/quickstart#get-your-api-key). ```bash cURL theme={"system"} curl -H "Authorization: Bearer API_KEY" \ -X GET https://api.ayrshare.com/api ``` ```javascript JavaScript theme={"system"} headers: {"Authorization": "Bearer API_KEY"} ``` ```python Python theme={"system"} headers = {"Authorization": "Bearer API_KEY"} ``` ```php PHP theme={"system"} $headers = ["Authorization" => "Bearer API_KEY"]; ``` ```go Go theme={"system"} headers := map[string]string{"Authorization": "Bearer API_KEY"} ``` ```ruby Ruby theme={"system"} headers = {"Authorization": "Bearer API_KEY"} ``` ```java Java theme={"system"} headers = {"Authorization": "Bearer API_KEY"} ``` ```csharp C# theme={"system"} headers = {"Authorization": "Bearer API_KEY"} ``` ```rust Rust theme={"system"} headers = {"Authorization": "Bearer API_KEY"} ``` > [!NOTE] > > Obtain your secret API Key in the Ayrshare dashboard under the API Key page found in the left navigation panel. > > For example, if your API Key is 2MPXPKQ-S03M5LS-GR5RX5G-AZCK8EA > > Your header should include: > > `Authorization: Bearer 2MPXPKQ-S03M5LS-GR5RX5G-AZCK8EA` ### Profile Key Format A Profile Key is used to interact on behalf of a User Profile. This is only available for Business or Enterprise plans. `Profile-Key: PROFILE_KEY` replacing PROFILE\_KEY with a user's [Profile Key](/docs/multiple-users/manage-user-profiles#get-the-profile-key). **Including a Profile Key in the header is required to interact on behalf of a User Profile**. A missing Primary Profile API Key or using the Profile Key in place of the API Key in the header will result in an error. Here's how to structure the headers for API requests: ```bash cURL theme={"system"} curl -H "Authorization: Bearer API_KEY" \ -H "Profile-Key: PROFILE_KEY" \ -X GET https://api.ayrshare.com/api ``` ```javascript JavaScript theme={"system"} headers: { "Authorization": "Bearer API_KEY", "Profile-Key": "PROFILE_KEY" } ``` ```python Python theme={"system"} headers = { "Authorization": "Bearer API_KEY", "Profile-Key": "PROFILE_KEY" } ``` ```php PHP theme={"system"} $headers = [ "Authorization" => "Bearer API_KEY", "Profile-Key" => "PROFILE_KEY" ]; ``` ```go Go theme={"system"} headers := map[string]string{ "Authorization": "Bearer API_KEY", "Profile-Key": "PROFILE_KEY" } ``` ```ruby Ruby theme={"system"} headers = { "Authorization": "Bearer API_KEY", "Profile-Key": "PROFILE_KEY" } ``` ```java Java theme={"system"} headers = { "Authorization": "Bearer API_KEY", "Profile-Key": "PROFILE_KEY" } ``` ```csharp C# theme={"system"} headers = { "Authorization": "Bearer API_KEY", "Profile-Key": "PROFILE_KEY" } ``` ```rust Rust theme={"system"} headers = { "Authorization": "Bearer API_KEY", "Profile-Key": "PROFILE_KEY" } ``` > [!NOTE] > > [Find the Profile Key](/docs/multiple-users/manage-user-profiles#get-the-profile-key) in the Ayrshare Dashboard under the Profile Key page found in the left navigation panel. > Switch to the Profile you want to use in the User Profile page. > Additionally, the Profile Key is returned in the response of the [Create a User Profile](/docs/apis/profiles/create-profile) endpoint. > > You can then use the Profile Key in the header of your requests: > > For example, if your Profile Key is `AX1XGG-9jK3M5LS-GR5RX5G-LLCK8EA` > > Your header should include both the API and Profile Key: > > `Authorization: Bearer 2MPXPKQ-S03M5LS-GR5RX5G-AZCK8EA` > > `Profile-Key: AX1XGG-9jK3M5LS-GR5RX5G-LLCK8EA` ### X/Twitter BYO Credentials Starting March 31, 2026, all X/Twitter operations through Ayrshare require your own OAuth 1.0a credentials. After linking your X account via OAuth, pass these 2 headers alongside your `Authorization` (and optional `Profile-Key`) headers for any request that targets X/Twitter: | Header | Description | | - | - | | `X-Twitter-OAuth1-Api-Key` | Your OAuth 1.0a API Key (Consumer Key) | | `X-Twitter-OAuth1-Api-Secret` | Your OAuth 1.0a API Key Secret (Consumer Secret) | These headers are required after March 31, 2026. Requests to X/Twitter endpoints without valid BYO credentials will be rejected. > [!NOTE] > > **One key pair, used for every request.** You create one X Developer App per Ayrshare account and use the same `X-Twitter-OAuth1-Api-Key` and `X-Twitter-OAuth1-Api-Secret` values on every X-targeting request, regardless of which sub-profile (`Profile-Key`) the request is for. You do not generate or rotate keys per customer or per end-user. See the [X BYO Key Setup Guide](/docs/dashboard/connect-social-accounts/x-twitter-byo-keys) for step-by-step instructions, code examples, and troubleshooting. The same two headers apply when driving X/Twitter through the [MCP Server](/docs/additional/mcp-action-server) — see [Connect & Setup](/docs/additional/mcp-action-connect). ## Content Type The Content Type should always be set as `Content-Type: "application/json"` unless the endpoint specifically specifies otherwise. ```bash cURL theme={"system"} curl -H "Authorization: Bearer API_KEY" \ -H "Profile-Key: PROFILE_KEY" \ # Optional -H "Content-Type: application/json" \ -X GET https://api.ayrshare.com/api ``` ```javascript JavaScript theme={"system"} headers: { "Authorization": "Bearer API_KEY", "Profile-Key": "PROFILE_KEY", // Optional "Content-Type": "application/json" } ``` ```python Python theme={"system"} headers = { "Authorization": "Bearer API_KEY", "Profile-Key": "PROFILE_KEY", # Optional "Content-Type": "application/json" } ``` ```php PHP theme={"system"} $headers = [ "Authorization" => "Bearer API_KEY", "Profile-Key" => "PROFILE_KEY", # Optional "Content-Type" => "application/json" ]; ``` ```go Go theme={"system"} headers := map[string]string{ "Authorization": "Bearer API_KEY", "Profile-Key": "PROFILE_KEY", // Optional "Content-Type": "application/json" } ``` ```ruby Ruby theme={"system"} headers = { "Authorization": "Bearer API_KEY", "Profile-Key": "PROFILE_KEY", # Optional "Content-Type": "application/json" } ``` ```java Java theme={"system"} headers = { "Authorization": "Bearer API_KEY", "Profile-Key": "PROFILE_KEY", // Optional "Content-Type": "application/json" } ``` ```csharp C# theme={"system"} headers = { "Authorization": "Bearer API_KEY", "Profile-Key": "PROFILE_KEY", // Optional "Content-Type": "application/json" } ``` ```rust Rust theme={"system"} headers = { "Authorization": "Bearer API_KEY", "Profile-Key": "PROFILE_KEY", // Optional "Content-Type": "application/json" } ``` ## Compression Ayrshare supports compression for all API requests. To enable compression, set the `Accept-Encoding` header to `deflate, gzip, br`. ```bash theme={"system"} Accept-Encoding: "deflate, gzip, br" ``` This is recommended for calling larger responses such as the [/history endpoint](/docs/apis/history/overview).
    • Only responses over 1024 bytes (1KB) are compressed.
    • The order of compression used: Brotli (br) first, then gzip, then deflate. Brotli is the most efficient compression algorithm.
    • The response header contains the content encoding used. For example: `content-encoding: br` if Brotli was used.
    • Learn more about compression and the benefits of using compression in the [Ayrshare Blog](https://www.ayrshare.com/blog/http-compression-in-node-js-a-dive-into-gzip-deflate-and-brotli/).
    > [!TIP] > > Don't forget to properly uncompress the response using the content encoding. ```bash cURL theme={"system"} curl -H "Authorization: Bearer API_KEY" \ -H "Profile-Key: PROFILE_KEY" \ # Optional -H "Content-Type: application/json" \ -H "Accept-Encoding: deflate, gzip, br" \ -X GET https://api.ayrshare.com/api ``` ```javascript JavaScript theme={"system"} headers: { "Authorization": "Bearer API_KEY", "Profile-Key": "PROFILE_KEY", // Optional "Content-Type": "application/json", "Accept-Encoding": "deflate, gzip, br" } ``` ```python Python theme={"system"} headers = { "Authorization": "Bearer API_KEY", "Profile-Key": "PROFILE_KEY", # Optional "Content-Type": "application/json", "Accept-Encoding": "deflate, gzip, br" } ``` ```php PHP theme={"system"} $headers = [ "Authorization" => "Bearer API_KEY", "Profile-Key" => "PROFILE_KEY", # Optional "Content-Type" => "application/json", "Accept-Encoding" => "deflate, gzip, br" ]; ``` ```go Go theme={"system"} headers := map[string]string{ "Authorization": "Bearer API_KEY", "Profile-Key": "PROFILE_KEY", // Optional "Content-Type": "application/json", "Accept-Encoding": "deflate, gzip, br" } ``` ```ruby Ruby theme={"system"} headers = { "Authorization": "Bearer API_KEY", "Profile-Key": "PROFILE_KEY", # Optional "Content-Type": "application/json", "Accept-Encoding": "deflate, gzip, br" } ``` ```java Java theme={"system"} headers = { "Authorization": "Bearer API_KEY", "Profile-Key": "PROFILE_KEY", // Optional "Content-Type": "application/json", "Accept-Encoding": "deflate, gzip, br" } ``` ```csharp C# theme={"system"} headers = { "Authorization": "Bearer API_KEY", "Profile-Key": "PROFILE_KEY", // Optional "Content-Type": "application/json", "Accept-Encoding": "deflate, gzip, br" } ``` ```rust Rust theme={"system"} headers = { "Authorization": "Bearer API_KEY", "Profile-Key": "PROFILE_KEY", // Optional "Content-Type": "application/json", "Accept-Encoding": "deflate, gzip, br" } ``` ## ID Types There are several types of IDs returned, which can be used in various endpoints: ### Ayrshare Post ID This ID is generated by Ayrshare and returned from the [/post endpoint](/docs/apis/post/post) in the `id` field. This ID makes it easy to get analytics on the post across social networks, add comments to the post, delete the post, etc. This is the ID you will use most often. ### Social Post ID Each social network assigns their own unique ID to posts and comments. These IDs are returned in the `postIds` field of the /post or /comments endpoints. You can use these IDs, or ones you get directly from the social networks, to retrieve data, such as with the [analytics social post ID](/docs/apis/analytics/social-by-id). ### Ayrshare Comment ID This ID is generated by Ayrshare and returned from the [/comments endpoint](/docs/apis/comments/post-comment) in the `id` field. This ID makes it easy to get analytics on the comment across social networks, add replies to the comment, delete the comment, etc. This is the ID you will use most often. This is often used if you want to get details on a particular comment published via Ayrshare. ### Social Comment ID Each social network assigns their own unique ID to comments. These IDs are returned in the `commentId` field of the [GET /comments endpoint](/docs/apis/comments/get-comments). This is often used if you want to get details on a particular comment published outside of Ayrshare. ## Error Codes Errors will return with [standard HTTP status codes](https://tools.ietf.org/html/rfc2616#section-10). For more information: > [card] [HTTP Status Codes](/docs/errors/errors-http) > > Learn more about HTTP status codes Detailed Errors are in the REST API response specific for each type of call. For more information: > [card] [Ayrshare Error Codes](/docs/errors/errors-ayrshare) > > Learn more about Ayrshare-specific error codes ## Timestamp Format Ayrshare uses Zulu Time, also known as UTC (Coordinated Universal Time) or an ISO 8601 formatted date string, for precise and unambiguous time references across different time zones. For example, use format `YYYY-MM-DDThh:mm:ssZ` and send as `2026-07-08T12:30:00Z`. Please see [utctime](https://www.utctime.net/) for more examples. You can convert the UTC format to your local time in the programming language of your choice. For example in JavaScript: ```javascript theme={"system"} const convertToLocalTime = (isoString) => { // Create a new Date object from the ISO string const date = new Date(isoString); // Extract local time components const localDate = date.toLocaleDateString(); const localTime = date.toLocaleTimeString(); // Combine the local date and time const localDateTime = `${localDate} ${localTime}`; return localDateTime; }; ``` ## Postman You can use Postman to test your REST API calls. > [card] [Postman](/docs/testing/postman) > > Learn how to use Postman with our API ## Random Posts, Images, and Videos Check out the [quick start guide](/docs/quickstart#publish-test-posts) on how to send random posts, images, or videos when testing. ## Packages We have both Node.js & Python packages, Bubble.io, Airtable, and Make guides available to make the RESTful calls easier. > [card] [Node NPM Package](/docs/packages-guides/nodejs) > > Integrate using our NodeJS package > [card] [Python PyPI Package](/docs/packages-guides/python) > > Integrate using our Python package > [card] [Airtable Guide](/docs/packages-guides/airtable) > > Learn how to use Ayrshare with Airtable > [card] [Bubble Plugin & Guide](/docs/packages-guides/bubble) > > Learn how to use Ayrshare with Bubble.io > [card] [Make Guide](/docs/packages-guides/make) > > Learn how to use Ayrshare with Make > [card] [Notion Guide](/docs/packages-guides/notion) > > Learn how to use Ayrshare with Notion > [card] [FlutterFlow Guide](/docs/packages-guides/flutterflow) > > Learn how to use Ayrshare with FlutterFlow > [card] [Retool Guide](/docs/packages-guides/retool) > > Learn how to use Ayrshare with Retool # Ayrshare Social Media API Source: https://www.ayrshare.com/docs/introduction One Ayrshare API connects 14 social networks to schedule posts, get analytics, manage comments, send direct messages, and run ads. Features vary by network. ![Welcome to Ayrshare](https://mintcdn.com/ayrshare-docs/WBlJ7RQgRrktEf-7/images/welcome-light.webp?fit=max\&auto=format\&n=WBlJ7RQgRrktEf-7\&q=85\&s=a0a905c20770325861e97b4ca3f8677a) Ayrshare is a unified API that lets you manage your users' social media presence across all major platforms with a single integration. Instead of dealing with 14 different social media APIs, you use just one to schedule posts, get analytics, manage comments, send direct messages, create ads, and more. ## What is Ayrshare? Managing multiple social media platforms is complex. Each network has its own API, approval process, error handling, and data formats. Keeping up with constant API changes across platforms is a full-time job. Ayrshare solves this by providing a single, [unified API](/docs/apis/overview) that handles all the complexity for you. We maintain direct integrations with each social network, so you can focus on building your product instead of managing infrastructure. With Ayrshare, you can: * Schedule and publish posts across multiple platforms * Retrieve analytics and engagement metrics * Manage comments and conversations * Send and receive direct messages * Create and manage advertising campaigns And with our [Launch](/docs/multiple-users/business-launch-overview) and [Business](/docs/multiple-users/business-plan-overview) plans, you can manage social media for all your users. ## See It In Action Here's how simple it is to post to multiple platforms on behalf of your user: ```json theme={"system"} // One API call posts to multiple platforms POST /post { "post": "Check out our new product launch! 🚀", "platforms": ["x", "facebook", "instagram", "linkedin", "tiktok"], "mediaUrls": ["https://example.com/video.mp4"], "scheduleDate": "2025-12-01T10:00:00Z" } ``` That's it! And you can then make a single call to [get the analytics](/docs/apis/analytics/overview) or [add a comment](/docs/apis/comments/overview) on all of these posts. See below for [how to get started](/docs/introduction#how-to-get-started-with-ayrshare). ## Which Social Networks Are Supported? Ayrshare seamlessly integrates with 14 major social networks: > [card] Bluesky > [card] Facebook > [card] Google Business > [card] Instagram > [card] LinkedIn > [card] Pinterest > [card] Reddit > [card] Snapchat > [card] Telegram > [card] Threads > [card] TikTok > [card] WhatsApp > [card] X (Twitter) > [card] YouTube Ayrshare integrates directly with each social network's official APIs and partnership programs. This ensures our platform delivers the most reliable, secure, and current social media management features available, all while maintaining compliance with each network's policies and best practices. > [!NOTE] > > The messaging API supports Instagram, Facebook, WhatsApp, and X. The Ads API supports Facebook. ## API-First Platform At Ayrshare, everything is designed with developers in mind, allowing you to quickly integrate social media features into your platform or application. Our API-first approach means every feature is accessible programmatically, with comprehensive documentation, SDKs in multiple languages, and predictable behavior. ## How It Works The API follows REST principles, making it intuitive for developers: * **GET** requests retrieve information (analytics, post history, user data) * **POST** requests create new resources (posts, comments, messages) * **PUT** requests update existing resources * **DELETE** requests remove resources All data is exchanged in JSON format, ensuring easy parsing and manipulation in any programming language. ## How to Get Started with Ayrshare Getting up and running takes just a few minutes: **1. Create Your Account** Sign up for Ayrshare and find your API key in the dashboard. **2. Connect Social Accounts** Link the social media accounts you want to manage. > [card] [Connect Social Media Accounts](/docs/dashboard/connect-social-accounts/overview) **3. Make Your First API Call** Test the API with our interactive documentation. > [card] [API Overview](/docs/apis/overview) **4. Follow the Quick Start** Get posting in minutes with our step-by-step guide. > [card] [Quick Start Guide](/docs/quickstart) **5. Scale to Multiple Users** Learn how to manage social media for all your users. > [card] [Business Plans for Multiple Users](/docs/multiple-users/business-plan-overview) **6. Get Support** Have questions? Our team is here to help via chat or email. # Quick Start Guide Source: https://www.ayrshare.com/docs/quickstart Connect social accounts, find your API Key, and publish a first post with /post, then follow best practices for error codes, key security, and test posts. The guide is for setting up a single company. For Business and Enterprise Plan subscriptions with multiple users, a personalized integration guide is provided. [Ayrshare's Social API Quick Start Guide](https://www.youtube.com/embed/HaWh_QOib_Y) ## Connect Your Social Media Accounts Log in or sign up for an [Ayrshare account](https://app.ayrshare.com/). On the Social Account page, click the social networks you want to connect. Please be sure to grant all permissions. ![Facebook Linking Example](https://mintcdn.com/ayrshare-docs/dKBti0xVDJrOZ19f/images/fb-connect.webp?fit=max\&auto=format\&n=dKBti0xVDJrOZ19f\&q=85\&s=d256f4862cb21b388d388cc62aa94659) ## Get Your API Key An API Key is required to authorize access to the API endpoints. This key is used in the header of your requests, and should be preceded by the `Bearer` keyword. Your API Key is located within the [Ayrshare Dashboard](https://app.ayrshare.com/), on the **API Key** page. If necessary, switch to your 'Primary Profile' via the **User Profiles** page. From there, you can find the **API Key** page in the left-hand side panel. ![API Key](https://mintcdn.com/ayrshare-docs/Nmrhj2Gh7WSf62Bh/images/api-key.webp?fit=max\&auto=format\&n=Nmrhj2Gh7WSf62Bh\&q=85\&s=29ebd3dd2f23c9f9f1c52a974ebd6240) Every API call must include the API Key in the Header using a `Bearer` Token. Also include the `Content-Type` of `application/json`. ```json Header theme={"system"} "Authorization": "Bearer API_KEY", "Content-Type": "application/json" ``` See the [Authorization](/docs/apis/overview#authorization) for more information. ## Send Your First API Post ### Publishing the Post Below are code examples showing how to make a post using our API in different programming languages. For an even easier integration, you can use one of our pre-built [SDK packages](/docs/packages-guides/overview). To make a post, you'll need:
    • Your [API Key](/docs/apis/overview#authorization) for authentication.
    • The text content of your post.
    • The social media platforms you want to post to (only include platforms you've linked).
    • Optional: URLs of any images or videos you want to include.
    All examples use the [/post](/docs/apis/post/post) endpoint. Let's look at some sample code: > [!NOTE] > > **Posting to X/Twitter?** X requires your own API credentials. Add 2 BYO headers (`X-Twitter-OAuth1-Api-Key` and `X-Twitter-OAuth1-Api-Secret`) to your request. This is a **one-time setup per Ayrshare account**, and the same key pair works for every sub-profile / end-user. See the [setup guide](/docs/dashboard/connect-social-accounts/x-twitter-byo-keys) for details. ```bash cURL theme={"system"} curl \ -H "Authorization: Bearer API_KEY" \ -H 'Content-Type: application/json' \ -d '{ "post": "Today is a great day!", "platforms": ["facebook", "instagram", "linkedin"], "mediaUrls": ["https://img.ayrshare.com/012/gb.jpg"] }' \ -X POST https://api.ayrshare.com/api/post ``` ```javascript JavaScript theme={"system"} const API_KEY = "API_KEY"; fetch("https://api.ayrshare.com/api/post", { method: "POST", headers: { "Content-Type": "application/json", "Authorization": `Bearer ${API_KEY}` }, body: JSON.stringify({ post: "Today is a great day!", // required platforms: ["bluesky", "facebook", "instagram", "linkedin"], // required mediaUrls: ["https://img.ayrshare.com/012/gb.jpg"] //optional }), }) .then((res) => res.json()) .then((json) => console.log(json)) .catch(console.error); ``` ```python Python theme={"system"} import requests payload = {'post': 'Today is a great day!', 'platforms': ['bluesky', 'facebook', 'instagram', 'linkedin'], 'mediaUrls': ['https://img.ayrshare.com/012/gb.jpg']} headers = {'Content-Type': 'application/json', 'Authorization': 'Bearer API_KEY'} r = requests.post('https://api.ayrshare.com/api/post', json=payload, headers=headers) print(r.json()) ``` ```php PHP theme={"system"} "Today is a great day!", "platforms" => ["bluesky", "facebook", "instagram", "linkedin", "pinterest"], "mediaUrls" => ["https://img.ayrshare.com/012/gb.jpg"] ]; curl_setopt_array($curl, [ CURLOPT_URL => 'https://api.ayrshare.com/api/post', CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_POSTFIELDS => json_encode($data), CURLOPT_HTTPHEADER => [ 'Authorization: Bearer API_KEY', // Replace 'API_KEY' with your actual API key 'Content-Type: application/json' ], ]); $response = curl_exec($curl); curl_close($curl); echo $response; ``` ```go Go theme={"system"} package main import ( "bytes" "encoding/json" "log" "net/http" ) func main() { message := map[string]interface{}{ "post": "Today is a great day!", "platforms": []string{"bluesky", "facebook", "instagram", "linkedin"}, "mediaUrls": []string{"https://img.ayrshare.com/012/gb.jpg"} } bytesRepresentation, err := json.Marshal(message) if err != nil { log.Fatalln(err) } req, _ := http.NewRequest("POST", "https://api.ayrshare.com/api/post", bytes.NewBuffer(bytesRepresentation)) req.Header.Add("Content-Type", "application/json; charset=UTF-8") req.Header.Add("Authorization", "Bearer API_KEY") res, err := http.DefaultClient.Do(req) if err != nil { log.Fatal("Error:", err) } res.Body.Close() } ``` ```csharp C# theme={"system"} using System; using System.Net.Http; using System.Text; using System.Threading.Tasks; namespace PostPOSTRequest_csharp { class Post { private static readonly HttpClient client = new HttpClient(); static async Task Main(string[] args) { string API_KEY = "API_KEY"; string url = "https://api.ayrshare.com/api/post"; // Set up request headers client.DefaultRequestHeaders.Add("Authorization", "Bearer " + API_KEY); // Prepare JSON content string json = "{\"post\" : \"Today is a great day!\"," + "\"platforms\" : [ \"bluesky\", \"facebook\", \"instagram\", \"linkedin\" ]," + "\"mediaUrls\" : [ \"https://img.ayrshare.com/012/gb.jpg\" ]}"; var content = new StringContent(json, Encoding.UTF8, "application/json"); try { // Send POST request HttpResponseMessage response = await client.PostAsync(url, content); response.EnsureSuccessStatusCode(); // Read response string responseBody = await response.Content.ReadAsStringAsync(); Console.WriteLine(responseBody); } catch (HttpRequestException e) { Console.WriteLine($"Error: {e.Message}"); } } } } ``` ```ruby Ruby theme={"system"} require 'httparty' # gem install httparty res = HTTParty.post("https://api.ayrshare.com/api/post", headers: {Authorization: "Bearer API_KEY"}, body: { post: "Today is a great day!", platforms: ['bluesky', 'facebook', 'instagram', 'linkedin'], mediaUrls: ["https://img.ayrshare.com/012/gb.jpg"] }).body puts res ``` Congratulations! You just sent your first post. ### Post Response You will receive a response with a `status` of "success" when our API successfully processes your post. The response includes:
    • `status`: Overall status of the API call ("success" or "error")
    • `errors`: Array of any errors encountered (empty if successful)
    • `postIds`: Array of objects containing platform-specific details:
      • Platform name
      • Post ID on that platform
      • URL to view the post
      • Additional platform-specific information
    Each platform in the `postIds` array will have its own status, allowing you to verify which platforms successfully received your post. Check your social media pages to ensure that the post was successfully processed and is live. Note that some platforms may have a slight delay before posts appear publicly. Here is a sample response: ```json Post Response theme={"system"} { "status": "success", "errors": [], "postIds": [ { "status": "success", "id": "738681876342836_1530521371228093", "postUrl": "https://www.facebook.com/738681876342836/posts/1530521371228093", "platform": "facebook" }, { "status": "success", "id": "18008340650526653", "postUrl": "https://www.instagram.com/p/DGf4hgZObR0/", "usedQuota": 1, "platform": "instagram" }, { "status": "success", "id": "urn:li:share:7300150903578275840", "postUrl": "https://www.linkedin.com/feed/update/urn:li:share:7300150903578275840", "owner": "urn:li:organization:66755239", "platform": "linkedin" } ], "id": "K0DmYRgugS5P694it", "refId": "d93ca2784c9bf7bf83e0bf081d16c91598", "post": "Today is a great day!" } ``` See more code examples of calling the social media API in Node.js, Python, PHP, Golang, C#, and Ruby, plus other great capabilities: > [card] [Post](/docs/apis/post/post) ### Diving a Little Deeper Let's break down the process of what just happened when we made this API call. **1. Set the Header** We set the Header Authorization bearer token (API Key) and content type of json. **2. Create a Body Object** Next, we created a body object:
    • Post containing the text "Today is a great day!".
    • Social network platforms of Facebook, Instagram, and LinkedIn. You should only include the platforms you linked in the Social Linkage Page.
    • Added an image in the mediaUrls.
    **3. Call the /post Endpoint** Sent everything as an HTTP POST to the /post endpoint. ## Before You Go, Some Best Practices ### Read the Docs We maintain extensive and up-to-date documentation. Since you are already here, take some time to check out the sections of the docs in the left navigation. ### Error Codes Are Important You need to handle them so your users have a great experience. > [card] [Errors](/docs/errors/overview) ### Keep Your API Key Secure We take security very seriously, and so should you. You have full control over the connection between Ayrshare and your social media accounts. Once the accounts are connected, the Ayrshare API key becomes the way you authenticate. Please keep this key secret and secure. ### Publish Test Posts You can publish test posts with a random quote, image, or video using the `randomPost`, `randomMediaUrl`, and `isLandscapeVideo` or `isPortraitVideo` parameters in the [/post endpoint](/docs/apis/post/post). If you use the `randomPost`, the `post` parameter is not required. [Post Random Content](https://www.youtube.com/embed/hMI0eK-5qBk?si=y5dkygosZ1B2DtlV) > [!WARNING] > > These posts are live on your social media pages, so if appropriate, delete them after testing. #### Random Quote Send a random quote for testing publishing a post by adding `randomPost: true`. Used with the [/post](/docs/apis/post/post) endpoint. ```json theme={"system"} "randomPost": true ``` #### Random Image Send a random image by adding `randomMediaUrl: true`. Used with the [/post](/docs/apis/post/post) endpoint. ```json theme={"system"} "randomMediaUrl": true ``` #### Random Video Send a random landscape video by adding `isVideo: true` and `isLandscapeVideo: true`. The video will be standard landscape size. Used with the [/post](/docs/apis/post/post) endpoint. ```json theme={"system"} "randomMediaUrl": true, "isLandscapeVideo": true ``` Send a random portrait video, required for TikTok or Facebook/Instagram Reels, by adding `isVideo: true` and `isPortraitVideo: true`. Used with the [/post](/docs/apis/post/post) endpoint. ```json theme={"system"} "randomMediaUrl": true, "isPortraitVideo": true ``` #### Random Comment Send a random comment for testing publishing a comment by adding `randomComment: true`. Used with the [/comment](/docs/apis/comments/post-comment) endpoint. ```json theme={"system"} "randomComment": true ``` #### Example cURL Call Send a random quote and portrait video. ```bash theme={"system"} curl \ -H "Authorization: Bearer API_KEY" \ -H "Content-Type: application/json" \ -X POST \ -d '{ "randomPost": true, "platforms": ["facebook", "instagram", "linkedin"], "randomMediaUrl": true, "isPortraitVideo": true }' \ https://api.ayrshare.com/api/post ``` ### **Use Postman to Test Your API Calls** [Postman](https://www.postman.com/) is an amazing tool for testing API calls. You can even generate code in over a dozen languages. ### Demo Code If you are looking for sample demo project in React and NodeJS, please see this GitHub repository: > [card] [Github Social Posting Demo App](https://github.com/ayrshare/social-api-demo) ### **Common Troubleshooting** Sometimes things don't go right, so here is a little help in our help center. > [card] [Troubleshooting](/docs/help-center/overview) ### **Reach Out To Us for Help** We are here to help, so please use the chat in the bottom right corner or [email us](mailto:support@ayrshare.com). # Get Instagram audio metadata Source: https://www.ayrshare.com/docs/apis/media/get-instagram-audio GET /media/instagram/audio/{audioId} Retrieve one Instagram audio track by audioId, with title, artist, duration, and artwork. Needs Instagram linked via Facebook Login, else code 514. Retrieve the details of one audio track by its `audioId`. Use this to confirm a stored id is still valid, or to show a track's title and artwork before publishing. > [!WARNING] > > **Requires Instagram linked through Facebook Login.** Instagram's Audio API is only available to accounts connected via **Facebook Login**, which needs a Facebook Page connected to the Instagram account. Accounts connected with **Instagram Login** (the Ayrshare default) cannot search, look up, or attach audio, and every audio request returns `code: 514`. > > This is a Meta restriction, not an Ayrshare one. Meta's documentation states the Audio API "is only available on the Instagram API with Facebook Login. It is not supported on the Instagram API with Instagram Login." No permission or plan upgrade changes it. > > **Three things are required:** > > 1. An Instagram **Business** or **Creator** account > > 2. A **Facebook Page connected** to that Instagram account > > 3. Instagram linked to Ayrshare with **Instagram Login disabled**, so the Facebook Login flow is used > > See the [step-by-step setup guide](/docs/dashboard/connect-social-accounts/instagram#music-attach-eligibility-facebook-login-required). ## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` **Profile-Key** `string` [Profile Key](/docs/apis/overview#profile-key-format) of a User Profile. Format: `Profile-Key: PROFILE_KEY` ## Path Parameters **audioId** `string` *required* The track id, as returned by [audio search](/docs/apis/media/search-instagram-audio). An id Instagram does not recognize returns `code: 517`. ## Response Fields The track's fields are returned flat on the response object rather than nested. Instagram does not return every field for every track, so treat all fields except `audioId` as optional. **status** `string` `success` when the track was found. **audioId** `string` The track id, echoed back. **title** `string` Track title. **displayArtist** `string` Artist name as Instagram displays it. **durationMs** `integer` Track length in milliseconds. **thumbnailUrl** `string` Cover artwork thumbnail URL. **audioType** `string` Either `music` or `original_sound`. **isAdsEligible** `boolean` Whether the track may be used in an ad. ```json 200: Success theme={"system"} { "status": "success", "audioId": "17901234567890123", "title": "Summer Nights", "displayArtist": "The Example Band", "durationMs": 32000, "thumbnailUrl": "https://scontent.example.com/cover.jpg", "audioType": "music", "isAdsEligible": true } ``` ```json 400: Track unavailable theme={"system"} { "status": "error", "code": 517, "action": "post", "message": "The selected audio track is unavailable. It may be an invalid audio id, restricted in your region, or not licensed for this use. Please choose a different track using the Instagram audio search endpoint and try again.", "platform": "instagram" } ``` > [!NOTE] > > **Availability is region-dependent.** Meta returns the same error for an id that does not exist, > one that is not licensed in the account's region, and one that is not licensed for this use. All > three report `517`, so a track that resolves for one account can legitimately fail for another. > Look up an id rather than assuming a stored one still works. ## Errors | Code | Meaning | | - | - | | 156 | Instagram is not linked to this profile. | | 161 | Instagram's authorization expired or was revoked. Relink the account. | | 239 | Instagram rejected the request for an account-level reason. | | 258 | Instagram returned a malformed response or an unmapped failure. | | 425 | The connection is missing a required Instagram permission. Relink and grant all permissions. | | 435 | Instagram's rate limit was reached. Wait before retrying. | | 514 | Instagram is linked with Instagram Login. The Audio API requires Facebook Login. | | 516 | The `audioId` is missing or empty. | | 517 | Instagram does not recognize the id, or the track is unavailable to this account. | Codes 514 to 518 are described in [Instagram Reels audio errors](/docs/errors/errors-ayrshare#instagram-reels-audio-errors); the rest are in the [Ayrshare error reference](/docs/errors/errors-ayrshare). ## Next Steps > [card] [Search for a track](/docs/apis/media/search-instagram-audio) > [card] [Attach the track to a Reel](/docs/apis/post/social-networks/instagram#adding-music-to-a-reel) # Get All Media in Gallery Source: https://www.ayrshare.com/docs/apis/media/get-media-in-gallery GET /media Retrieve all images and videos uploaded to your media gallery, including resized images, with each file's id, URL, size, file name, description, and expiration. Retrieve all the images uploaded to the image gallery including resized images. ## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` **Profile-Key** `string` [Profile Key](/docs/apis/overview#profile-key-format) of a User Profile. Format: `Profile-Key: PROFILE_KEY` ```bash cURL theme={"system"} curl \ -H "Authorization: Bearer [API Key]" \ -X GET https://api.ayrshare.com/api/media ``` ```javascript JavaScript theme={"system"} const API_KEY = "Your API Key"; fetch("https://api.ayrshare.com/api/media", { method: "GET", headers: { "Authorization": `Bearer ${API_KEY}` } }) .then((res) => res.json()) .then((json) => console.log(json)) .catch(console.error); ``` ```python Python theme={"system"} import requests headers = {'Authorization': 'Bearer [API_KEY]'} r = requests.get('https://api.ayrshare.com/api/media', headers=headers) print(r.json()) ``` ```php PHP theme={"system"} request( 'GET', 'https://api.ayrshare.com/api/media', [ 'headers' => [ 'Content-Type' => 'application/json', 'Authorization' => 'Bearer API_KEY' ] ] ); echo json_encode(json_decode($res->getBody()), JSON_PRETTY_PRINT); ``` ```csharp C# theme={"system"} using System; using System.Net.Http; using System.Threading.Tasks; namespace MediaGETRequest_csharp { class Media { static async Task Main(string[] args) { string API_KEY = "API_KEY"; string url = "https://api.ayrshare.com/api/media"; using (var client = new HttpClient()) { client.DefaultRequestHeaders.Add("Authorization", "Bearer " + API_KEY); try { HttpResponseMessage response = await client.GetAsync(url); response.EnsureSuccessStatusCode(); string responseBody = await response.Content.ReadAsStringAsync(); Console.WriteLine(responseBody); } catch (HttpRequestException e) { Console.WriteLine($"Error: {e.Message}"); } } } } } ``` ```javascript 200: Array of images in the image gallery theme={"system"} [ { "id": "5553a26a-cffb-4a31-bd89-dee296d35648-jpeg", "timeCreated": "2025-07-30T20:23:49.774Z", "size": "223340", "url": "https://images.ayrshare.com/LW8kx9Q5smXeJl5CujF7DxFAF1q1/5553a26a-cffb-4a31-bd89-dee296d35648.jpeg", "fileName": "test1.jpeg", "description": "Test1", "expiresAt": "2025-09-05T22:00:48.000Z" // The date and time the media will expire }, { "id": "93383cc7-7a15-4d16-a5fa-fdda1f9bebe2-jpeg", "timeCreated": "2025-07-30T20:24:57.421Z", "size": "223340", "url": "https://images.ayrshare.com/LW8kx9Q5smXeJl5CujF7DxFAF1q1/93383cc7-7a15-4d16-a5fa-fdda1f9bebe2.jpeg", "fileName": "test2.jpeg", "description": "Test2", "expiresAt": "2025-09-05T22:00:48.000Z" // The date and time the media will expire } ] ``` # Metadata on a media file Source: https://www.ayrshare.com/docs/apis/media/get-media-metadata GET /media/meta Retrieve the metadata on an externally accessible media file URL, such as its format, codec, width, height, duration in seconds, size in bytes, and type. Retrieve the metadata on a media file URL to check format, dimensions, and other details. ## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` ## Query Parameters **url** `string` *required* Encoded URI of the externally accessible media file URL. ```json 200: Success theme={"system"} { "codec": "h264", "duration": 28.24, // In seconds "filename": "https://img.ayrshare.com/random/landscape16.mp4", "format": "QuickTime / MOV", "height": 1080, "size": 16275959, // In bytes "type": "video", "width": 1920 } ``` ```json 400: Bad media URL theme={"system"} { "action": "post", "status": "error", "code": 229, "message": "Error validating video. Please check the video or URL." } ``` ```json 503: Media probe temporarily unavailable theme={"system"} { "action": "post", "status": "error", "code": 499, "message": "Ayrshare's media probe service is temporarily unavailable. Please retry shortly.", "retryAvailable": true } ``` # Media API Overview Source: https://www.ayrshare.com/docs/apis/media/overview Manage your image and video gallery by uploading and retrieving media, verifying media URLs, or skipping uploads by passing external URLs in mediaUrls. Manage your image and video gallery by uploading and retrieving images and videos. Please be sure to verify the media URL before using it to post. > [!TIP] > > If you already have your media accessible by an external URL, such as an S3 bucket, you can skip uploading the files to Ayrshare. Just POST to the `/post` endpoint with your externally accessible URL in the `mediaUrls` body parameter and your file will automatically be uploaded. # Resize an Image Source: https://www.ayrshare.com/docs/apis/media/resize POST /media/resize Use the resize endpoint to fit images to social network sizes, add watermarks, change backgrounds, apply effects, or crop using platform or dimensions. The social networks have [specific requirements](/docs/media-guidelines/overview) for social media images. The resize endpoint allows you to choose a social network compatible image size, add watermarks, change backgrounds, add effects, crop, and more. By default resizing will change the dimensions of an image, but not crop the image. You may instead crop the image. See below for details. ## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` ## Body Parameters **mediaUrl** `string` URL of image to be resized. Must begin with `https://`. Required if `file` not present. `imageUrl` also works. If both are sent, `imageUrl` is used. **platform** `string` One platform to resize for, for example `"instagram"`. Required if `dimensions` not present, except for watermark-only or format-only (`convertToJpg`, `convertToWebP`) requests. See [platform options](/docs/apis/media/resize#platform-options) for details. **file** `object` Send the media file as a multipart form-data object. Required if `mediaUrl` not present. **watermark** `object` URL and optional position of watermark to be applied to resized image. The watermark will appear in the lower right corner of the image by default. See [watermark](/docs/apis/media/resize#watermark) for details. **effects** `object` Change opacity, colors, etc. See [effects options](/docs/apis/media/resize#effects-options) for details. **dimensions** `object` Object specifying `width` and `height` for resizing. If cropping, you may optionally specify the center `x` and `y` coordinates. Default is the center of the image. ```json Dimensions theme={"system"} { "width": 500, "height": 500, "xCoordinate": 35, // optional for crop mode "yCoordinate": 50 // optional for crop mode } ``` > [!NOTE] > > Width and height required if platform is not specified. **mode** `string` (default: resize) Value: `resize`, `blur`, or `crop`. See [mode](/docs/apis/media/resize#mode) for details. **convertToJpg** `boolean` Automatically convert to a JPG file, such as from a PNG to a JPG file. 75% quality will be used. See [convert to a JPG](/docs/apis/media/resize#convert-to-a-jpg-or-webp) for details. **convertToWebP** `boolean` Automatically convert to a WebP file, such as from a PNG to a WebP file. 75% quality will be used. See [convert to a WebP](/docs/apis/media/resize#convert-to-a-jpg-or-webp) for details. ### Platform Options Specify a platform as a String to use predefined dimensions of the image, or you may specify your own with the `dimensions` field. For example `"platform": "facebook"` will set the dimension of the image as width 1200px and height 630px.
    • `facebook`: 1200px width, 630px height.
    • `instagram`: 1080px width, 1080px height.
    • `instagram_landscape`: 1080px width, 680px height.
    • `instagram_portrait`: 1080px width, 1920px height.
    • `instagram_special`: 1080px width, 800px height.
    • `linkedin`: 1200px width, 627px height.
    • `pinterest`: 1080px width, 1920px height.
    • `tiktok`: 1080px width, 1920px height.
    • `twitter`: 1600px width, 900px height.
    Note, that resize to these dimensions will not crop the image. If you want to crop the image, you can use the `mode` parameter set to `crop` with the `dimensions` and `xCoordinate` and `yCoordinate` fields. ### Mode #### Resize Resize is the default mode that will change the dimensions of an image while maintaining its aspect ratio. Resize the image to the specified dimensions without cropping any content. Example JSON: ```json Resize theme={"system"} { "mediaUrl": "https://img.ayrshare.com/012/gb.jpg", "platform": "instagram", "mode": "resize" } ``` You can also specify custom dimensions using the `dimensions` field: ```json Resize with Dimensions theme={"system"} { "mediaUrl": "https://img.ayrshare.com/012/gb.jpg", "mode": "resize", "dimensions": { "width": 800, "height": 600 } } ``` Either the `platform` or the dimensions field `width` and `height` must be specified. #### Crop Crop will cut off "crop" the image to the specified dimensions. By default the center coordinate will be the center of the image. You may also specify your own x/y coordinates. Example JSON: ```json Crop theme={"system"} { "mediaUrl": "https://img.ayrshare.com/012/gb.jpg", "platform": "instagram", "mode": "crop" } ``` You can also specify custom dimensions and optional crop coordinates using the `dimensions` field: ```json Crop with Dimensions theme={"system"} { "mediaUrl": "https://img.ayrshare.com/012/gb.jpg", "mode": "crop", "dimensions": { "width": 1080, "height": 1080, "xCoordinate": 35, "yCoordinate": 50 } } ``` Either the `platform` or the dimensions field `width` and `height` must be specified. For square crops, if `width` or `height` are less than the dimensions of the provided image, the small of the `width` or `height` will be used. For example, if the image is 1200x800 and the crop requested is 1080x1080, the image returned will be 800x800. #### Blur Blur effect will duplicate the image as a background and blur the image. Example blur JSON: ```json Blur theme={"system"} { "mediaUrl": "https://img.ayrshare.com/012/gb.jpg", "platform": "instagram", "mode": "blur" } ``` Example blur image: ![Background with Blur](https://mintcdn.com/ayrshare-docs/Nmrhj2Gh7WSf62Bh/images/apis/media/blur.webp?fit=max\&auto=format\&n=Nmrhj2Gh7WSf62Bh\&q=85\&s=8877f9f348f319fc683bc7928d767dbb) ### Watermark #### Watermark Overview You may add a watermark to the image by providing a URL, which must begin with `https://` and an optional position. The watermark will by default appear in the bottom right corner of the image - `southeast`. We recommend a PNG with a transparent background. Example watermark JSON: ```json Watermark theme={"system"} { "mediaUrl": "https://img.ayrshare.com/random/photo-13.jpg", "platform": "instagram", "watermark": { "url": "https://img.ayrshare.com/012/100-percent.png", "position": "northeast" // optional } } ``` Example watermark image in southeast position: ![Add Watermark](https://mintcdn.com/ayrshare-docs/Nmrhj2Gh7WSf62Bh/images/apis/media/watermark.webp?fit=max\&auto=format\&n=Nmrhj2Gh7WSf62Bh\&q=85\&s=7794b66e76cfa988131ab99232af9081) #### Watermark Position The position of the watermark can be one of the following:
    • `north`
    • `northeast`
    • `east`
    • `southeast`
    • `south`
    • `southwest`
    • `west`
    • `northwest`
    • `center`
    ### Effects Options #### Color Hexadecimal Hexadecimal value for color of background for blur. Only applicable if `"mode": "blur"`. String value, e.g. `"#A020F0"` Example color background JSON: ```json Color Background theme={"system"} { "mediaUrl": "https://img.ayrshare.com/012/gb.jpg", "platform": "instagram", "mode": "blur", "effects": { "color": "#A020F0" } } ``` Example color background image: ![Add Colored Background](https://mintcdn.com/ayrshare-docs/Nmrhj2Gh7WSf62Bh/images/apis/media/color-background.webp?fit=max\&auto=format\&n=Nmrhj2Gh7WSf62Bh\&q=85\&s=07c06abcfac8e792c361cc8eaa9884a1) #### Color: Grayscale, Sepia, Invert You made change the primary image color by specifying `grayscale`, `sepia`, or `invert`. The field `"blur": true` is not required and should not be used if you do not want a background. Example grayscale JSON: ```json Grayscale theme={"system"} { "mediaUrl": "https://img.ayrshare.com/random/photo-13.jpg", "platform": "instagram", "effects": { "color": "grayscale" } } ``` Example grayscale image: ![Add Grayscale](https://mintcdn.com/ayrshare-docs/Nmrhj2Gh7WSf62Bh/images/apis/media/grayscale.webp?fit=max\&auto=format\&n=Nmrhj2Gh7WSf62Bh\&q=85\&s=c654e1485a00be0d421d4ffa19ebfbc7) #### Opacity Set the opacity of the image. Number value range: 0 - 1. Example opacity JSON: ```json Opacity theme={"system"} { "effects": { "opacity": 0.2 } } ``` #### Quality For JPG or JEPG images specify the quality, or amount of compression, of the image. The lower the number the more compressed, but lower the image quality. The higher the number the less compressed, but the higher the image quality. Number value range: 0 - 100. Example quality JSON: ```json Quality theme={"system"} { "effects": { "quality": 20 } } ``` ### Convert to a JPG or WebP The `convertToJpg` and `convertToWebP` options allow you to transform images from their original format (such as PNG) to JPG or WebP format respectively. By default, the converted images will have a quality setting of 75%. You can customize the compression level by using the [quality](/docs/apis/media/resize#quality) parameter in the effects object. Note that if your source image is already a JPG and you use `convertToJpg`, the API will simply resize the image to your specified dimensions without changing the format. Example convert to JPG: ```json Convert to JPG theme={"system"} { "convertToJpg": true } ``` Example convert to WebP: ```json Convert to WebP theme={"system"} { "convertToWebP": true } ``` ```bash cURL theme={"system"} curl \ -H "Authorization: Bearer API_KEY" \ -H 'Content-Type: application/json' \ -d '{"mediaUrl": "https://img.ayrshare.com/012/gb.jpg", "platform": "instagram"}' \ -X POST https://api.ayrshare.com/api/media/resize ``` ```javascript JavaScript theme={"system"} const API_KEY = "API_KEY"; fetch("https://api.ayrshare.com/api/media/resize", { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${API_KEY}` }, body: JSON.stringify({ mediaUrl: "https://img.ayrshare.com/012/gb.jpg", // required platform: "instagram" }) }) .then((res) => res.json()) .then((json) => console.log(json)) .catch(console.error); ``` ```python Python theme={"system"} import requests payload = {'mediaUrl': 'https://img.ayrshare.com/012/gb.jpg', 'platform': 'instagram'} headers = {'Content-Type': 'application/json', 'Authorization': 'Bearer API_KEY'} r = requests.post('https://api.ayrshare.com/api/media/resize', json=payload, headers=headers) print(r.json()) ``` ```php PHP theme={"system"} "https://img.ayrshare.com/012/gb.jpg", "platform" => "instagram" ); curl_setopt_array($curl, array( CURLOPT_URL => 'https://api.ayrshare.com/api/media/resize', CURLOPT_RETURNTRANSFER => true, CURLOPT_ENCODING => '', CURLOPT_MAXREDIRS => 10, CURLOPT_TIMEOUT => 0, CURLOPT_FOLLOWLOCATION => true, CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, CURLOPT_CUSTOMREQUEST => 'POST', CURLOPT_POSTFIELDS => http_build_query($data), CURLOPT_HTTPHEADER => array( 'Authorization: Bearer API_KEY', 'Accept-Encoding: gzip' ), )); $response = curl_exec($curl); curl_close($curl); echo $response; ``` ```csharp C# theme={"system"} using System; using System.Net.Http; using System.Text; using System.Text.Json; using System.Threading.Tasks; public class AyrshareApiClient { private readonly HttpClient _httpClient; private readonly string _apiKey; private const string BaseUrl = "https://api.ayrshare.com/api"; public AyrshareApiClient(string apiKey) { _apiKey = apiKey ?? throw new ArgumentNullException(nameof(apiKey)); _httpClient = new HttpClient(); _httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {_apiKey}"); } public async Task ResizeMediaAsync(string mediaUrl, string platform) { try { var requestData = new { mediaUrl = mediaUrl, platform = platform }; var content = new StringContent( JsonSerializer.Serialize(requestData), Encoding.UTF8, "application/json" ); var response = await _httpClient.PostAsync($"{BaseUrl}/media/resize", content); response.EnsureSuccessStatusCode(); var jsonResponse = await response.Content.ReadAsStringAsync(); return jsonResponse; } catch (HttpRequestException ex) { throw new Exception($"Failed to resize media: {ex.Message}", ex); } } public void Dispose() { _httpClient.Dispose(); } } ``` ```json 200: Success theme={"system"} { "status": "success", "url": "https://media.ayrshare.com/9abf1426d6ce9122ef11c72bd62e59807c5cc083/8UbyBjHTxgHkAC1I37e6O.jpg", "platform": "instagram", "mode": "blur", "effects": { "color": "#A020F0" } } ``` ```json 400: Failed Resize theme={"system"} { "action": "resize", "status": "error", "code": 312, "message": "Invalid extension type. Extension: null. Please verify the extension is one of the following: png, jpg, jpeg and the file is accessible." } ``` # Search Instagram audio Source: https://www.ayrshare.com/docs/apis/media/search-instagram-audio GET /media/instagram/audio/search Search Instagram's licensed audio catalog for a track to attach to a Reel Search Instagram's audio catalog by keyword. Use the returned `audioId` with [`instagramOptions.audioConfiguration`](/docs/apis/post/social-networks/instagram#adding-music-to-a-reel) to attach the track when you publish a Reel. > [!WARNING] > > **Requires Instagram linked through Facebook Login.** Instagram's Audio API is only available to accounts connected via **Facebook Login**, which needs a Facebook Page connected to the Instagram account. Accounts connected with **Instagram Login** (the Ayrshare default) cannot search, look up, or attach audio, and every audio request returns `code: 514`. > > This is a Meta restriction, not an Ayrshare one. Meta's documentation states the Audio API "is only available on the Instagram API with Facebook Login. It is not supported on the Instagram API with Instagram Login." No permission or plan upgrade changes it. > > **Three things are required:** > > 1. An Instagram **Business** or **Creator** account > > 2. A **Facebook Page connected** to that Instagram account > > 3. Instagram linked to Ayrshare with **Instagram Login disabled**, so the Facebook Login flow is used > > See the [step-by-step setup guide](/docs/dashboard/connect-social-accounts/instagram#music-attach-eligibility-facebook-login-required). ## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` **Profile-Key** `string` [Profile Key](/docs/apis/overview#profile-key-format) of a User Profile. Format: `Profile-Key: PROFILE_KEY` ## Query Parameters **query** `string` *required* Keyword to search for, such as an artist or track name. An empty or whitespace-only value returns `code: 101`. **audioType** `string` (default: music) Which catalog to search. Either `music` for licensed commercial tracks or `original_sound` for audio taken from other creators' Reels. Any other value returns `code: 101`. **limit** `integer` Maximum number of tracks to return, from `1` to `100`. Non-integer or out-of-range values return `code: 101`. When omitted, Instagram's own default applies. ## Response Fields Instagram does not return every field for every track, so treat all track fields except `audioId` as optional. **status** `string` `success` when the search completed. An empty catalog match is still a success. **query** `string` The keyword that was searched, echoed back. **audio** `array` Matching tracks. An empty array means the search ran and matched nothing. **Track fields** **audioId** `string` The id to pass as `audioConfiguration.audioId` when publishing a Reel. **title** `string` Track title. **displayArtist** `string` Artist name as Instagram displays it. **durationMs** `integer` Track length in milliseconds. **thumbnailUrl** `string` Cover artwork thumbnail URL. **audioType** `string` Either `music` or `original_sound`. **isAdsEligible** `boolean` Whether the track may be used in an ad. Licensed music is frequently unavailable for ads even when it is available for an organic Reel. ```json 200: Success theme={"system"} { "status": "success", "query": "summer", "audio": [ { "audioId": "17901234567890123", "title": "Summer Nights", "displayArtist": "The Example Band", "durationMs": 32000, "thumbnailUrl": "https://scontent.example.com/cover.jpg", "audioType": "music", "isAdsEligible": true } ] } ``` ```json 200: No matches theme={"system"} { "status": "success", "query": "asdfghjkl", "audio": [] } ``` ```json 400: Instagram Login account theme={"system"} { "status": "error", "code": 514, "action": "post", "message": "Adding music to a Reel requires an Instagram account connected via Facebook Login. This profile is connected with Instagram Login, which Meta does not permit for music attach, search, or lookup. Please relink Instagram using Facebook Login and grant the instagram_content_publish permission, then try again.", "platform": "instagram" } ``` ## Errors | Code | Meaning | | - | - | | 101 | Missing or empty `query`, an unrecognized `audioType`, or a `limit` outside 1 to 100. | | 156 | Instagram is not linked to this profile. | | 161 | Instagram's authorization expired or was revoked. Relink the account. | | 239 | Instagram rejected the request for an account-level reason, including an unavailable track. On search this reports 239 rather than the 517 the lookup endpoint returns. | | 258 | Instagram returned a malformed response or an unmapped failure. | | 425 | The connection is missing a required Instagram permission. Relink and grant all permissions. | | 435 | Instagram's rate limit was reached. Wait before retrying. | | 514 | Instagram is linked with Instagram Login. The Audio API requires Facebook Login. | Codes 514 to 518 are described in [Instagram Reels audio errors](/docs/errors/errors-ayrshare#instagram-reels-audio-errors); the rest are in the [Ayrshare error reference](/docs/errors/errors-ayrshare). ## Next Steps > [card] [Attach the track to a Reel](/docs/apis/post/social-networks/instagram#adding-music-to-a-reel) > [card] [Look up a single track](/docs/apis/media/get-instagram-audio) # Upload Large Media Files Source: https://www.ayrshare.com/docs/apis/media/upload-large-media GET /media/uploadUrl For file uploads greater than 10 MB, obtain a presigned URL to upload a file For file uploads greater than 10 MB, obtain a presigned URL to upload a file.
    • Maximum file upload size 5 GB.
    • Upload presigned URL valid for 1 hour after being generated.
    • Access URL available for 30 days after uploaded. All published posts are unaffected at the social networks. Scheduled posts beyond that time frame will result in errors at time of publishing.
    > [!TIP] > > If you already have your media accessible by an external URL, such as an S3 bucket, you can skip uploading the files to Ayrshare. Just POST to the `/post` endpoint with your externally accessible URL in the `mediaUrls` body parameter and your file will automatically be uploaded. ## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` ## Query Parameters **fileName** `string` If the contentType is not present, then a full file name with extension is required. Name of the file to be uploaded. Must include an extension such as .png, .jpg, .mov, .mp4, etc. **contentType** `string` The content-type of the media being uploaded. Valid formats include: `mp4`, `mov`, `png`, `jpg`, or `jpeg`. For example, if the file is a Quicktime .mov file, then the contentType should be `mov`. If not present, the type is taken from the `fileName` extension. If neither gives a supported type, the request returns error `301`. ```bash cURL theme={"system"} curl \ -H "Authorization: Bearer [API Key]" \ -X GET https://api.ayrshare.com/api/media/uploadUrl?fileName=test.mov&contentType=mov ``` ```javascript JavaScript theme={"system"} const API_KEY = "Your API Key"; fetch("https://api.ayrshare.com/api/media/uploadUrl?fileName=test.mov&contentType=mov", { method: "GET", headers: { "Authorization": `Bearer ${API_KEY}` } }) .then((res) => res.json()) .then((json) => console.log(json)) .catch(console.error); ``` ```python Python theme={"system"} import requests headers = {'Authorization': 'Bearer [API_KEY]'} r = requests.get('https://api.ayrshare.com/api/media/uploadUrl?fileName=test.mov&contentType=mov', headers=headers) print(r.json()) ``` ```php PHP theme={"system"} request( 'GET', 'https://api.ayrshare.com/api/media/uploadUrl?fileName=test.mov&contentType=mov', [ 'headers' => [ 'Content-Type' => 'application/json', 'Authorization' => 'Bearer API_KEY' ] ] ); echo json_encode(json_decode($res->getBody()), JSON_PRETTY_PRINT); ``` ```csharp C# theme={"system"} using System; using System.Net.Http; using System.Threading.Tasks; using System.Web; namespace MediaUploadUrlGETRequest_csharp { class MediaUploadUrl { static async Task Main(string[] args) { string API_KEY = "API_KEY"; string baseUrl = "https://api.ayrshare.com/api/media/uploadUrl"; // Build URL with query parameters var uriBuilder = new UriBuilder(baseUrl); var query = HttpUtility.ParseQueryString(string.Empty); query["fileName"] = "test.mov"; query["contentType"] = "mov"; uriBuilder.Query = query.ToString(); using (var client = new HttpClient()) { client.DefaultRequestHeaders.Add("Authorization", "Bearer " + API_KEY); try { HttpResponseMessage response = await client.GetAsync(uriBuilder.Uri); response.EnsureSuccessStatusCode(); string responseBody = await response.Content.ReadAsStringAsync(); Console.WriteLine(responseBody); } catch (HttpRequestException e) { Console.WriteLine($"Error: {e.Message}"); } } } } } ``` ## Response Details
    • `accessUrl` is the URL to access the media file after upload.
    • `contentType` is the content-type set for the media being upload. Use this in the *Content-Type* header when uploading the media.
    • `uploadUrl` is the URL used to *PUT* the media file. Please see below.
    ### Additional Endpoint Examples The process to upload larger files:
    • Obtain an `uploadURL` and `accessURL` via the `/media/uploadUrl` endpoint. Please see above.
    • Upload the file via a *PUT* with *Content-Type* set to the returned `contentType`.
    • Upload the media by using the `--upload-file` with a media file and the `uploadUrl`.
    • On a successful upload, a `200` response will be returned.
    • After uploading the media file, POST to the `/post` endpoint with the `accessUrl` in the `mediaUrls` body parameter.
    • **The presigned upload URL may only be uploaded to once**. If you sent a bad file you must create a new upload URL. No error response will occur if the file is not successfully uploaded. See below of [verifying the URL exists](/docs/apis/media/verify-media-url).
    ```bash cURL theme={"system"} curl -X PUT \ -H 'Content-Type: video/mp4' \ --upload-file LOCAL_FILE_PATH uploadUrl ``` ```javascript JavaScript theme={"system"} const fs = require("fs").promises; const uploadFileToSignedUrl = async (signedUrl, filePath) => { try { const fileBuffer = await fs.readFile(filePath); const response = await fetch(signedUrl, { method: "PUT", body: fileBuffer, headers: { "Content-Type": "video/mp4" } }); if (response.ok) { console.log("File upload successful:", response.status); } else { console.error("File upload failed:", response.status); } } catch (error) { console.error("Error uploading file:", error); } }; // Use the signed URL generated from the previous step const signedUrl = "SIGNED_URL"; const filePath = "LOCAL_FILE_PATH"; uploadFileToSignedUrl(signedUrl, filePath); ``` ```python Python theme={"system"} import requests def upload_file_to_signed_url(signed_url, file_path): try: with open(file_path, 'rb') as file: response = requests.put(signed_url, data=file, headers={'Content-Type': 'video/mp4'}) if response.ok: print("File upload successful:", response.status_code) else: print("File upload failed:", response.status_code) except Exception as error: print("Error uploading file:", error) # Use the signed URL generated from the previous step signed_url = "SIGNED_URL" file_path = "LOCAL_FILE_PATH" upload_file_to_signed_url(signed_url, file_path) ``` ```php PHP theme={"system"} getMessage(); } } // Use the signed URL generated from the previous step $signedUrl = "SIGNED_URL"; $filePath = "LOCAL_FILE_PATH"; uploadFileToSignedUrl($signedUrl, $filePath); ?> ``` ```csharp C# theme={"system"} using System; using System.Net.Http; using System.IO; using System.Threading.Tasks; class Program { static async Task Main(string[] args) { string signedUrl = "SIGNED_URL"; // Replace with your signed URL string filePath = "LOCAL_FILE_PATH"; // Replace with your file path try { await UploadFileToSignedUrl(signedUrl, filePath); } catch (Exception ex) { Console.WriteLine("Error uploading file: " + ex.Message); } } static async Task UploadFileToSignedUrl(string signedUrl, string filePath) { using (var client = new HttpClient()) using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read)) using (var content = new StreamContent(fileStream)) { content.Headers.Add("Content-Type", "video/mp4"); var response = await client.PutAsync(signedUrl, content); if (response.IsSuccessStatusCode) { Console.WriteLine("File upload successful: " + response.StatusCode); } else { Console.WriteLine("File upload failed: " + response.StatusCode); } } } } ``` ```java Java theme={"system"} import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.net.http.HttpRequest.BodyPublishers; import java.nio.file.Path; import java.io.IOException; import java.nio.file.Files; public class FileUploader { public static void main(String[] args) { String signedUrl = "SIGNED_URL"; // Replace with your signed URL String filePath = "LOCAL_FILE_PATH"; // Replace with your file path try { uploadFileToSignedUrl(signedUrl, filePath); } catch (IOException | InterruptedException e) { System.out.println("Error uploading file: " + e.getMessage()); } } private static void uploadFileToSignedUrl(String signedUrl, String filePath) throws IOException, InterruptedException { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(signedUrl)) .header("Content-Type", "image/jpg") .PUT(BodyPublishers.ofFile(Path.of(filePath))) .build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); if (response.statusCode() == 200) { System.out.println("File upload successful: " + response.statusCode()); } else { System.out.println("File upload failed: " + response.statusCode()); } } } ``` Please be sure that the `contentType` set when creating the `uploadUrl` matches the Content-Type and the file type when PUTing the file. For example, if you set the `contentType` to "image/png" when creating the `uploadUrl`, be sure to set the `Content-Type: image/png` and the uploaded file ends in `.png`. On a successful upload, a `200` response will be returned. ### Example Upload File Binary in Node.js Here is an example of uploading a binary media file using Node with JavaScript: ```javascript theme={"system"} const fs = require("fs"); const request = require("request"); const API_KEY = "Your API Key"; const fileName = "test.png"; const endpoint = `https://api.ayrshare.com/api/media/uploadUrl?fileName=${fileName}&contentType=png`; const run = async () => { request.get( { headers: { "Content-Type": "application/json", Authorization: `Bearer ${API_KEY}` }, url: endpoint }, (err, res, body) => { if (err) { return console.error(err); } const json = JSON.parse(body); console.log("Upload URL:", json); return fs.createReadStream(`./${fileName}`).pipe( request.put( json.uploadUrl, { headers: { "Content-Type": json.contentType } }, (err, httpsResponse, body) => { if (err) { console.error("err", err); } else { console.log(body); } } ) ); } ); }; run(); ``` ### Example Upload Binary File in Postman You may also use Postman to upload the binary file to the `uploadUrl`. In Postman: 1. Select the HTTP Method `PUT`. 2. Paste your `uploadUrl` in the url field. Note, the url will expire after an hour and may only be used once. If you make a call and it fails, you must regenerate the `uploadUrl`. 3. In *Headers* set the `Content-Type` to be the content type returned in the /uploadUrl endpoint. For example: `Content-Type: image/png`. 4. Select *Body -> binary* and select the file to upload. 5. Press **Send**. 6. **Important**: No return response will occur, so you should check if the upload was successful by opening the `accessUrl` returned from the /uploadUrl endpoint in a browser. You may also use the [verify URL endpoint](/docs/apis/media/verify-media-url). ![Postman Binary Upload](https://mintcdn.com/ayrshare-docs/Nmrhj2Gh7WSf62Bh/images/apis/validate/postman%20binary.webp?fit=max\&auto=format\&n=Nmrhj2Gh7WSf62Bh\&q=85\&s=ca3def769242814afb17a58d9268667f) > [card] [Postman Sample JSON File]() ```javascript 200: An upload URL and access URL theme={"system"} { "accessUrl": "https://media.ayrshare.com/Aswmfs3dIEbwLSdhTlV2/test.mp4", "contentType": "video/mp4", "uploadUrl": "https://storage.googleapis.com/..." } ``` ```json 400: Bad Request Error getting signed URL theme={"system"} { "action": "upload", "status": "error", "code": 301, "message": "The provided content-type 'movd' is not recognized." } ``` # Upload Image or Video Source: https://www.ayrshare.com/docs/apis/media/upload-media POST /media/upload Upload an image or small video as multipart form data or Base64 encoded file to get a URL for the /post endpoint, with a 30 MB limit and 90 day storage. This endpoint allows you to upload a file or an image or small video to include in your post. Returned will be the URL to the image that can be used in the /post endpoint. You can pass the file either as a ***multipart form data*** as a form parameter or a **Base64 encoded file** as a body parameter. Important notes about media uploads: 1. For best performance, we recommend
    • Hosting media files on your own server (e.g. AWS S3).
    • Passing the media URL directly in the `mediaUrls` parameter of the [/post](/docs/apis/post/overview) endpoint.
    • This approach is faster than uploading files through this endpoint.
    2. Media file retention
    • Uploaded files are stored for 90 days.
    • After 90 days:
      • Published posts on social networks are unaffected.
      • Scheduled posts will fail to publish if they reference expired media.
    3. File size limits
    • Maximum file size: 30 MB.
    • For larger files, see our guide on [handling large media uploads](/docs/apis/media/upload-large-media).
    > [!TIP] > > If you already have your media accessible by an external URL, such as an S3 bucket, you can skip uploading the files to Ayrshare. Just POST to the `/post` endpoint with your externally accessible URL in the `mediaUrls` body parameter and your file will automatically be uploaded. ## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` **Content-Type** `string` *required* Use `multipart/form-data` if sending a multipart form data - see below. Otherwise, send the standard `application/json`. ## Body Parameters **file** `string or object` *required* Max 30 MB file size. We recommend sending as a multipart form-data object instead of Base64 encoding. **fileName** `string` The name of the file to be uploaded. **description** `string` A description of the file. ### Send as Multipart Form-Data Send the media file as a multipart form-data object. Please be sure to specify the `Content-Type` as mentioned above. ### Send as Base64 Send the media file as a Base64 encoded string as a Data URI string. The string should begin with `data:content/type;base64` Example encoding with Output Format Data URI:
    • Image: [https://base64.guru/converter/encode/image](https://base64.guru/converter/encode/image)
    • Video: [https://base64.guru/converter/encode/video](https://base64.guru/converter/encode/video)
    Note: The /post endpoint accepts larger files via an external URL with the `mediaUrls` parameter. ```bash cURL theme={"system"} # Send as Multipart Form-Data curl \ -H "Authorization: Bearer API_KEY" \ -F "file=@test.png" \ -F "fileName=test.png" \ -F "description=best image" \ -X POST https://api.ayrshare.com/api/media/upload # Send as Base64 curl \ -H "Authorization: Bearer API_KEY" \ -H 'Content-Type: application/json' \ -d '{"file": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQ...", "fileName": "test.png", "description": "best image"}' \ -X POST https://api.ayrshare.com/api/media/upload ``` ```javascript JavaScript theme={"system"} // Send as Multipart Form-Data const FormData = require('form-data'); const fs = require('fs'); const API_KEY = "API_KEY"; const imagePath = './test.png'; const form = new FormData(); form.append('file', fs.createReadStream(imagePath)); form.append('fileName', 'test.png'); form.append('description', 'best image'); fetch("https://api.ayrshare.com/api/media/upload", { method: "POST", headers: { "Authorization": `Bearer ${API_KEY}` // Don't set Content-Type header - FormData will set it automatically with boundary }, body: form }) .then(res => res.json()) .then(json => console.log(json)) .catch(console.error); // Send as Base64 const API_KEY = "API_KEY"; const base64 = "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQ..."; fetch("https://api.ayrshare.com/api/media/upload", { method: "POST", headers: { "Content-Type": "application/json", "Authorization": `Bearer ${API_KEY}` }, body: JSON.stringify({ file: base64, fileName: "test.png", description: "best image" }), }) .then((res) => res.json()) .then((json) => console.log(json)) .catch(console.error); ``` ```python Python theme={"system"} # Send as Multipart Form-Data import requests # For a local file: files = { 'file': ('test.png', open('test.png', 'rb')), } # Form data data = { 'fileName': 'test.png', 'description': 'best image' } headers = { 'Authorization': 'Bearer API_KEY' } r = requests.post( 'https://api.ayrshare.com/api/media/upload', files=files, data=data, headers=headers ) print(r.json()) # Send as Base64 import requests payload = {'file': 'data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQ...', 'fileName': "test.png", 'description': "best image"} headers = {'Content-Type': 'application/json', 'Authorization': 'Bearer API_KEY'} r = requests.post('https://api.ayrshare.com/api/media/upload', json=payload, headers=headers) print(r.json()) ``` ```php PHP theme={"system"} # Send as Multipart Form-Data [ 'method' => 'POST', 'header' => "Authorization: Bearer API_KEY\r\n" . "Content-Type: multipart/form-data; boundary=" . $boundary . "\r\n" . "Content-Length: " . strlen($data) . "\r\n", 'content' => $data ] ]; // Send the request $context = stream_context_create($options); $result = file_get_contents('https://api.ayrshare.com/api/media/upload', false, $context); // Print the response echo $result; # Send as Base64 'data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQ...', 'fileName' => "test.png", 'description' => "best image" ]; $ch = curl_init('https://api.ayrshare.com/api/media/upload'); curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data)); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_HTTPHEADER, [ 'Content-Type: application/json', 'Authorization: Bearer API_KEY' ]); $response = curl_exec($ch); curl_close($ch); echo json_encode(json_decode($response), JSON_PRETTY_PRINT); ``` ```javascript 200: Success theme={"system"} { "id": "1167335b-6c37-4fc6-ab8a-044e0005d335-jpeg", "url": "https://images.ayrshare.com/q3Ls85VTsrbODnGIJHpy7PaHWwA3/1167335b-6c37-4fc6-ab8a-044ed885d.jpeg", "fileName": "fun.jpg", "description": "good times" } ``` ```json 400: Bad Request Error in upload theme={"system"} { "action": "request", "status": "error", "code": 101, "message": "Missing or incorrect parameters. Please verify with the docs. .../ayrshare.com/rest-api/endpoints" } ``` # Verify Media URL Exists Source: https://www.ayrshare.com/docs/apis/media/verify-media-url POST /media/urlExists Verify that a media file URL exists with a HEAD request and get its contentType. Use it with /media/uploadUrl; it checks reachability, not file contents. Verify that the media file exists when uploaded. It should be used in conjunction with the /media/uploadUrl endpoint. ## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` ## Verify Media URL Exists `POST` `https://api.ayrshare.com/api/media/urlExists` Verify that the media file exists when uploaded. It should be used in conjunction with the /media/uploadUrl endpoint. You will also receive the `contentType` of the media file. A `HEAD` request is made to the media URL to verify it exists. Please be sure the hosting provider is not blocking the `HEAD` request. This endpoint checks reachability only. It does not run the deeper media probe used before publishing, validate the file's contents or platform requirements, or prove that a social network's crawler can download the media. A successful response can therefore still be followed by a media-validation error during `/post`. ## Body Parameters **mediaUrl** `string` *required* URL of the media to verify exists. ```bash cURL theme={"system"} curl \ -H "Authorization: Bearer API_KEY" \ -H 'Content-Type: application/json' \ -d '{"mediaUrl": "https://img.ayrshare.com/012/vid.mp4"}' \ -X POST https://api.ayrshare.com/api/media/urlExists ``` ```javascript JavaScript theme={"system"} const API_KEY = "API_KEY"; fetch("https://api.ayrshare.com/api/media/urlExists", { method: "POST", headers: { "Content-Type": "application/json", "Authorization": `Bearer ${API_KEY}` }, body: JSON.stringify({ mediaUrl: "https://img.ayrshare.com/012/vid.mp4" }), }) .then((res) => res.json()) .then((json) => console.log(json)) .catch(console.error); ``` ```python Python theme={"system"} import requests payload = {'mediaUrl': 'https://img.ayrshare.com/012/vid.mp4'} headers = {'Content-Type': 'application/json', 'Authorization': 'Bearer API_KEY'} r = requests.post('https://api.ayrshare.com/api/media/urlExists', json=payload, headers=headers) print(r.json()) ``` ```php PHP theme={"system"} request( 'POST', 'https://api.ayrshare.com/api/media/urlExists', [ 'headers' => [ 'Content-Type' => 'application/json', 'Authorization' => 'Bearer API_KEY' ], 'mediaUrl' => 'https://img.ayrshare.com/012/vid.mp4' ] ); echo json_encode(json_decode($res->getBody()), JSON_PRETTY_PRINT); ``` ```json 200: OK Media URL is valid theme={"system"} { "status": "success", "statusCode": 200, "contentType": "image/jpeg" } ``` ```json 404: Not Found Media file not found theme={"system"} { "status": "error", "statusCode": 404, "statusText": "Not Found" } ``` # Get Auto Response Source: https://www.ayrshare.com/docs/apis/messages/get-auto-response GET /messages/autoresponse Get the auto response setting, including whether it is active, the message text, and wait seconds. If no settings exist, only a status is returned. Get the auto response setting. If the auto response settings do not exist, no data, except a `status`, will be returned. ## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` **Profile-Key** `string` [Profile Key](/docs/apis/overview#profile-key-format) of a User Profile. Format: `Profile-Key: PROFILE_KEY` ```json 200: Success theme={"system"} { "status": "success", "autoResponseActive": true, "autoResponseMessage": "Howdy Buckaroo!", "autoResponseWaitSeconds": 30 } ``` ```json 200: No Settings theme={"system"} { "status": "success" } ``` # Get Messages Source: https://www.ayrshare.com/docs/apis/messages/get-messages GET /messages/:platform Get messages or conversations for Facebook, Instagram, X/Twitter, or WhatsApp, with filters for status, conversationId, and limit, plus caching details. > [!NOTE] > > **Targeting X/Twitter?** Starting March 31, 2026, all X operations require your own API credentials. After linking X via OAuth, include these 2 headers in your request: > > `X-Twitter-OAuth1-Api-Key` — Your API Key (Consumer Key) > > `X-Twitter-OAuth1-Api-Secret` — Your API Key Secret (Consumer Secret) > > **One-time setup per Ayrshare account.** You create one X Developer App and reuse the same API Key and Secret across every sub-profile / end-user you link. You do *not* create a new app per customer. > > Not linked yet? See the [full setup guide](/docs/dashboard/connect-social-accounts/x-twitter-byo-keys) to connect your X account. > > Your keys are never logged or stored by Ayrshare. Get messages or conversations for a messaging platform. Retrieval times differ on each social network. On Facebook, Instagram, and WhatsApp, messages are available via Ayrshare in real time. On X/Twitter, there is a delay of up to 3 minutes to see new message updates. Please contact support to learn more about the Enterprise Plan if you need real-time X/Twitter message access. > [!NOTE] > > **Response caching:** For **Facebook** and **Instagram**, responses are cached for **60 > seconds**. For **X/Twitter**, responses are cached for **15 seconds** to better support polling. > WhatsApp reads messages already received through Meta webhooks, so a newly received message can > appear as soon as Ayrshare processes its webhook. The response still includes `lastUpdated` and > `nextUpdate` metadata. > [!NOTE] > > Initial message history retrieval for Facebook and Instagram is limited to the > last 20 messages. Please see the [Message History Retrieval for Facebook and > Instagram](/docs/apis/messages/overview#message-history-retrieval-for-facebook-and-instagram) > section for more information. > [!NOTE] > > WhatsApp conversations are identified by the correspondent's phone number > (digits only, E.164 without the leading `+`). A stored outbound message may include a `status` > value of `sent`, `delivered`, `read`, or `failed`. > [!NOTE] > > WhatsApp messages sent through [Send Message](/docs/apis/messages/send-message) are not currently > added to Get Messages history. Incoming WhatsApp messages received through webhooks are stored > and returned here. ## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` **Profile-Key** `string` [Profile Key](/docs/apis/overview#profile-key-format) of a User Profile. Format: `Profile-Key: PROFILE_KEY` ## Path Parameters **platform** `string` *required* The platform to get the message: `facebook`, `instagram`, `twitter`, `whatsapp` ## Query Parameters **status** `string` *required* (default: active) Return active conversations or archived conversations. Values: `active` or `archived`. **conversationId** `string` Only return the specific conversation. **action** `string` Only return messages in one direction. Values: `sent` or `received`. Omit to return both. **conversationsOnly** `boolean` (default: false) Return conversations instead of messages. Returns one page of conversations — follow `meta.pagination.next` for the rest. If `true`, `conversationId` is ignored. > [!NOTE] > > When `conversationsOnly=true`, conversation details are returned in > `converstationsDetails`. This spelling is part of the current API response. **limit** `integer` Page size for messages and for conversation lists: an integer from 1 to 100, with a default and maximum of **100**. On X/Twitter `limit` also chooses which set of messages you read. Omit it to page your stored messages, which is what every other platform does and what applies `status`, `action` and `conversationsOnly`. Supply it to page X's live timeline through X's own cursor instead, which applies none of those three filters. That choice is made by the **first** request only. Once you are following a cursor, the cursor decides: a stored cursor keeps returning stored messages even if you send `limit`, and an X cursor keeps returning the live timeline even if you omit it. You cannot switch between the two mid-pagination. **next** `string` Opaque cursor from `meta.pagination.next`. Pass it as `next` with the same profile, platform and filters. Continue while `meta.pagination.hasMore` is `true`, even if a filtered page comes back short or empty. `limit` may be omitted on subsequent requests and defaults to 100; on X/Twitter the cursor already records which set of messages it came from, so sending or omitting `limit` alongside it will not switch you to the other one. ### Fetching the next page Start with `GET /api/messages/instagram?limit=100`. To continue, URL-encode the returned `meta.pagination.next` value and send `GET /api/messages/instagram?limit=100&next=`. The parameter is `next`, not `cursor`. Keep the same Profile-Key and query filters throughout. Check `meta.pagination.hasMore`, not the number of results, to decide when to stop. A filtered request may return fewer than `limit` results, or an empty page with a continuation, because each request scans a bounded portion of history. Messages in stored history are ordered newest first, with document ID breaking ties. Conversation lists are ordered by document ID. Paging is not a frozen snapshot: new messages appear when you restart from the first page, and changing conversation statuses can change filtered results. Existing message fields and the `converstationsDetails` spelling are unchanged. Integrations that previously expected a complete list from one request — whether of messages or of conversations, on any platform — must follow the continuation cursor; increasing `limit` above 100 is not supported. ```bash cURL theme={"system"} curl \ -H "Authorization: Bearer API_KEY" \ -X GET https://api.ayrshare.com/api/messages/facebook ``` ```javascript JavaScript theme={"system"} const apiKey = 'API_KEY'; const url = 'https://api.ayrshare.com/api/messages/facebook'; const headers = { 'Authorization': `Bearer ${apiKey}`, }; fetch(url, { method: 'GET', headers: headers, }) .then(response => { if (response.ok) { return response.json(); } else { throw new Error(`Request failed. Status code: ${response.status}`); } }) .then(data => { console.log('Response:', data); }) .catch(error => { console.error('Error:', error.message); }); ``` ```python Python theme={"system"} import requests api_key = 'API_KEY' url = 'https://api.ayrshare.com/api/messages/facebook' headers = { 'Authorization': f'Bearer {api_key}', } response = requests.get(url, headers=headers) if response.status_code == 200: data = response.json() print('Response:', data) else: print(f'Request failed. Status code: {response.status_code}') ``` ```php PHP theme={"system"} ``` ```csharp C# theme={"system"} using System; using System.Net.Http; using System.Net.Http.Headers; using System.Threading.Tasks; using System.Text.Json; class Program { static async Task Main() { string apiKey = "API_KEY"; string url = "https://api.ayrshare.com/api/messages/facebook"; using var client = new HttpClient(); client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", apiKey); try { var response = await client.GetAsync(url); if (response.IsSuccessStatusCode) { var responseBody = await response.Content.ReadAsStringAsync(); var responseData = JsonSerializer.Deserialize(responseBody); Console.WriteLine("Response:"); Console.WriteLine(responseData); } else { Console.WriteLine($"Request failed. Status code: {response.StatusCode}"); } } catch (HttpRequestException ex) { Console.WriteLine($"Error: {ex.Message}"); } } } ``` ```java Java theme={"system"} import java.io.IOException; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; public class Main { public static void main(String[] args) { String apiKey = "API_KEY"; String url = "https://api.ayrshare.com/api/messages/facebook"; HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(url)) .header("Authorization", "Bearer " + apiKey) .GET() .build(); try { HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); if (response.statusCode() == 200) { String responseBody = response.body(); System.out.println("Response:"); System.out.println(responseBody); } else { System.out.println("Request failed. Status code: " + response.statusCode()); } } catch (IOException | InterruptedException e) { System.out.println("Error: " + e.getMessage()); } } } ``` ```json 200: Success theme={"system"} { "status": "success", "messages": [ { "senderId": "106638148652444", "senderDetails": { "name": "Ayrshare" }, "conversationId": "t_10161117434308444", "created": "2024-06-06T00:54:32.455Z", "action": "sent", "recipientId": "7101149746568444", "id": "m_JH6o-yS83JoxWmQaLrmgSaHwGtfTgQ", "message": "Howdy!", "platform": "facebook", "reactions": { "7101149746568522": "😆". // Reaction by the customer on the Howdy! message } }, { "recipientDetails": { "name": "Sara Smith", "id": "736532028017333", "picture": "https://img.ayrshare.com/333/messages/facebook-eTZzhE2b.jpeg" }, "senderId": "106638148652329", "attachments": [ { "type": "image", "url": "https://scontent.xx.fbcdn.net/v/t1.15752-9/490986808_1193328359195158" } ], "conversationId": "t_3567590438533", "created": "2024-06-06T00:54:32.455Z", "action": "sent", "recipientId": "736532028017333", "id": "m_WJlfgzopxfdRM1wFTKYHKv7zh75P", "updated": "2024-06-06T00:54:32.455Z", "platform": "facebook", "senderDetails": { "name": "Ayrshare" } }, { "senderId": "7101149746568444", "senderDetails": { "name": "John Smith", "profileImage": "https://platform-lookaside.fbsbx.com/platform/profilepic/" }, "conversationId": "t_10161117434308444", "created": "2024-06-06T00:54:28.102Z", "action": "received", "recipientId": "106638148652329", "id": "m_HGbotYJUmf4AzyPlJ-2uZqHwGtfTgQihX", "message": "Look up!", "platform": "facebook" }, { "senderId": "7101149746568444", "senderDetails": { "name": "John Smith", "profileImage": "https://platform-lookaside.fbsbx.com/platform/profilepic/" }, "conversationId": "t_10161117434308444", "created": "2024-06-06T00:49:11.679Z", "action": "received", "recipientId": "106638148652444", "id": "m_jXoYQIwTXaq2u06PG6Z8vaHwGtfTgQ", "message": "How is the weather?", "platform": "facebook" } ], "lastUpdated": "2024-06-09T21:46:04.233Z", "nextUpdate": "2024-06-09T21:47:04.233Z" } ``` ```json 200: Paginated (X/Twitter) theme={"system"} { "status": "success", "messages": [ { "id": "1893410668991234567", "conversationId": "1234567890-9876543210", "senderId": "9876543210", "created": "2024-06-09T21:30:00.000Z", "message": "Hey, how are you?", "action": "received", "senderDetails": { "name": "Jane Doe", "username": "janedoe" } }, { "id": "1893410668991234566", "conversationId": "1234567890-9876543210", "senderId": "1234567890", "created": "2024-06-09T21:28:00.000Z", "message": "Hello!", "action": "sent", "senderDetails": { "name": "My Account", "username": "myaccount" } } ], "messagesCount": 2, "lastUpdated": "2024-06-09T21:46:04.233Z", "nextUpdate": "2024-06-09T21:46:19.233Z", "meta": { "pagination": { "hasMore": true, "limit": 5, "next": "eyJwYWdpbmF0aW9uVG9rZW4iOiIxODkzNDEwNjY4OTkxMjM0NTY1In0=" } } } ``` ```json 200: WhatsApp theme={"system"} { "status": "success", "messages": [ { "id": "wamid.HBgLMTQxNTU1NTEyMzQVAgARGBI3MEM3Q0NDQjlGRTUzMjJBNEUA", "conversationId": "14155551234", "senderId": "14155551234", "recipientId": "123456789012345", "created": "2026-05-18T17:10:02.000Z", "message": "When is my order shipping?", "action": "received", "platform": "whatsapp" }, { "id": "wamid.HBgLMTQxNTU1NTEyMzQVAgARGBI4OUYxRkExNzE0M0EwQTYwM0EA", "conversationId": "14155551234", "senderId": "14155551234", "recipientId": "123456789012345", "created": "2026-05-18T17:08:41.000Z", "attachments": [ { "type": "image", "url": "https://img.ayrshare.com/abc123/whatsapp/whatsapp_image_x9aB2pK7.jpg" } ], "action": "received", "platform": "whatsapp" } ], "lastUpdated": "2026-05-18T17:13:00.000Z", "nextUpdate": "2026-05-18T17:14:00.000Z" } ``` ```json 200: WhatsApp Conversations theme={"system"} { "status": "success", "conversationIds": ["14155551234", "447700900123"], "converstationsDetails": [ { "id": "14155551234", "participant": { "id": "14155551234", "name": "Jane Customer" }, "status": "active", "unread": 2 }, { "id": "447700900123", "participant": { "id": "447700900123" }, "status": "active", "unread": 0 } ], "lastUpdated": "2026-05-18T17:13:00.000Z", "nextUpdate": "2026-05-18T17:14:00.000Z" } ``` ```json 200: Conversations theme={"system"} { "status": "success", "conversationIds": ["t_10161117434308444", "t_356759043857444"], "converstationsDetails": [ { "id": "t_10161117434308444", "participant": { "name": "John Smith", "id": "7101149746568444", "picture": "https://platform-lookaside.fbsbx.com/platform/profilepic/" }, "status": "active", "watermark": 1717889607444 }, { "id": "t_356759043857444", "participant": { "name": "Sara Johnson", "id": "7365320280173444", "picture": "https://platform-lookaside.fbsbx.com/platform/profilepic/" }, "status": "active" } ], "lastUpdated": "2024-06-09T21:46:04.233Z", "nextUpdate": "2024-06-09T21:47:04.233Z" } ``` ```json 403: Messaging Not Enabled theme={"system"} { "action": "messages", "status": "error", "code": 361, "message": "Messaging is not enabled for this User Profile. Please subscribe to Messaging and activate Messaging for this User Profile." } ``` ```json 400: Social Account Needs Relinking theme={"system"} { "action": "messages", "status": "error", "code": 362, "message": "The social network needs to be relinked to access Messaging. Please unlink, relink, and try again.", "resolution": { "relink": true, "platform": "facebook" } } ``` ```json 403: X App Missing Direct Message Permission theme={"system"} { "action": "messages", "status": "error", "code": 520, "message": "X rejected this request because the linked X app does not have Direct Message permission. In the X Developer Portal, set the User authentication settings for the app to \"Read and write and Direct messages\", then unlink and relink the X account. X tokens keep the permissions they were created with, so changing the app setting alone is not enough.", "resolution": { "relink": true, "platform": "twitter" } } ``` # Messages API Overview Source: https://www.ayrshare.com/docs/apis/messages/overview Send and receive direct messages on Facebook, Instagram, X/Twitter, and WhatsApp The Messaging API allows you to manage direct messages (DM) to correspondents who contact your User Profiles. A correspondent is the person with whom your user (User Profile) is communicating with. A conversation is a series of messages between your user (User Profile) and their correspondent.
    • The Messaging API is included with all Ayrshare Business Plans and as a paid add-on for Premium plans.
    • For both Business and Premium plans, you must activate Messaging in the Account page of the Ayrshare dashboard.
    • Messaging is available for Facebook Messenger, Instagram Direct Messenger, X Direct Messages, and WhatsApp (Private Beta).
    > [!NOTE] > > **WhatsApp is in Private Beta.** Send and receive WhatsApp messages through > the Messaging API. If you'd like early access, email > [lotty@ayrshare.com](mailto:lotty@ayrshare.com). > [card] [Learn more about the Messaging API](https://www.ayrshare.com/ayrshare-messaging-api-for-facebook-instagram-and-x-twitter/) ## Key Messaging Features
    • Manage your users' conversations with correspondents.
    • Sending text, image, video, and emoji messages on behalf of your users.
    • Retrieving complete conversation histories.
    • Setting up automated message responses.
    • Receiving real-time updates via webhooks for messages received, message reactions, read receipts.
    ## Enable Messaging ### Enable Your Ayrshare Account and User Profiles You must first enable messaging for your overall Ayrshare account to manage DMs. In the Ayrshare dashboard go to the [Account page](https://app.ayrshare.com/account) and click the "Learn More" button and then "Enable". At this point you have enabled messaging for your overall account, *but have not activated messaging for individual User Profiles*. You can activate messaging for individual User Profiles either in the [User Profiles page](https://app.ayrshare.com/manage-profiles) by clicking "Messaging Active" checkbox for each profile or using the create or update [profiles endpoint](/docs/apis/profiles/overview). Facebook and Instagram grant the messaging permissions when an account is linked, so activate messaging before you link accounts. This works the same whether you activate messaging in the dashboard or with the profiles endpoint: * **Accounts linked after messaging was activated** need no relink. * **Accounts linked before messaging was activated** need a relink if their "Messaging" badge shows as a warning instead of the normal badge. * **Accounts linked through Ayrshare Connect** (the [widget](/docs/multiple-users/connect-widget) or [direct mode](/docs/multiple-users/connect-direct-mode)) never get the messaging permissions. To use messaging, relink them on the User Profile's social linking page. Relink accounts on the [Social Accounts page](https://app.ayrshare.com/social-accounts) or on the User Profile's [social linking page](/docs/multiple-users/user-integration). To skip the relink, set `messagingActive: true` when you [create the User Profile](/docs/apis/profiles/create-profile), before linking any accounts. Accounts that are ready show the normal "Messaging" badge. > [!NOTE] > > The Messages endpoints report these setup steps with distinct error codes, so you can tell them apart > programmatically: > > * **`403` / code `361`** — Messaging is not enabled. Either the account is not subscribed to Messaging, or this > User Profile has not been activated. Complete the steps above; relinking a social account will not help. > * **`400` / code `362`** — Messaging is active for the User Profile, but this **Facebook or Instagram** account > still needs to be relinked. This response includes `resolution: { "relink": true }`. ![Messaging badge](https://mintcdn.com/ayrshare-docs/Nmrhj2Gh7WSf62Bh/images/apis/messaging/messaging-badge.webp?fit=max\&auto=format\&n=Nmrhj2Gh7WSf62Bh\&q=85\&s=cee009857e195b2835e913eb36c3b83f) ### Enable Messaging for WhatsApp > [!NOTE] > > **WhatsApp is in Private Beta.** The setup flow and Messaging API endpoints are available to > approved beta accounts. To request access, email > [lotty@ayrshare.com](mailto:lotty@ayrshare.com). Link a WhatsApp Business account through Meta's **embedded signup** flow directly from the Ayrshare dashboard:
    • You need a **Meta Business Manager** account, a **WhatsApp Business Account (WABA)**, and an eligible phone number. Meta verifies the account and phone number during embedded signup.
    • On the Social Accounts page, click **Link** on the WhatsApp tile. Ayrshare launches Meta's embedded signup pop-up which walks the customer through selecting/creating their Business Manager, WABA, and phone number, and accepting Meta's terms.
    • When signup finishes, Ayrshare verifies ownership of the selected WABA and phone number, completes registration, and subscribes the account to WhatsApp webhooks. You do not enter a separate registration PIN in the Ayrshare dashboard.
    ### Important WhatsApp-Specific Behavior
    • **Recipients are phone numbers, not IDs.** WhatsApp identifies correspondents by E.164 phone number (digits only, no `+`). Conversation IDs returned from Ayrshare are also the correspondent's phone number — there are no opaque conversation IDs the way Facebook and Instagram use.
    • **24-hour customer service window.** WhatsApp Business only allows free-form outbound messages within 24 hours of the most recent message from the correspondent. Outside that window Meta rejects the send. Templated messages outside the 24-hour window are not currently supported through Ayrshare.
    • **Address a recipient directly with the API.** Pass the recipient's phone number to [Send Message](/docs/apis/messages/send-message). Meta only delivers a free-form message when that recipient is inside the 24-hour customer service window. Approved templates are required for cold outreach, and Ayrshare does not currently expose template sending.
    • **Delivery status uses one field.** When available on a stored outbound message, [Get Messages](/docs/apis/messages/get-messages) returns `status` as `sent`, `delivered`, `read`, or `failed`. Delivery-status updates and WhatsApp reactions are not currently pushed to your registered webhook URL.
    • **Media types and sizes** follow Meta's published WhatsApp Cloud API limits — images up to 5 MB, audio and video up to 16 MB, and documents up to 100 MB at the time of writing.
    • **Incoming messages are marked as read.** Ayrshare marks each incoming WhatsApp message as read as soon as it arrives, so the sender sees it as read right away.
    • **Keep Messaging on for the receiving profile.** Incoming WhatsApp messages for a profile that does not have Messaging turned on are not stored or sent to your webhook. Turn it on from the Profiles page of the Ayrshare dashboard, or set `messagingActive` to `true` with [Update Profile](/docs/apis/profiles/update-profile).
    #### Supported WhatsApp Media
    • **Image:** `.jpg`, `.jpeg`, `.png`
    • **Video:** `.mp4`, `.3gp`
    • **Audio:** `.aac`, `.amr`, `.mp3`, `.m4a`, `.ogg`
    • **Document:** `.txt`, `.pdf`, `.ppt`, `.pptx`, `.doc`, `.docx`, `.xls`, `.xlsx`
    ### Enable Messaging in the Instagram App You may need to enable messaging for your Instagram account:
    • Go to your profile in the Instagram mobile app and tap the menu icon ≡ in the upper right corner to go to the **Settings and activity** page.
    • Scroll down to the **How others can interact with you** section.
    • Click **Messages and story replies**.
    • Tap **Message requests**.
    • Under **Connected tool** toggle on the **Allow access to messages** switch.
    > [!TIP] > > The **Message requests** setting shown here also governs whether a *recipient* can be reached. If you use [comment-triggered automations](/docs/apis/automations/overview) to DM people who comment on your posts, delivery depends on each recipient's Message requests setting — a message can be accepted by Instagram (`sent`) and then silently dropped. See [Automation DM Sent but Not Delivered](/docs/help-center/technical-support/automation-dm-sent-but-not-delivered). ## Important Information on Messaging > [!NOTE] > > **For Facebook and Instagram, a conversation must be initiated by the correspondent.** Once a > conversation is established, you may then send messages, receive messages, get reactions (e.g. > thumbs ups), or get read receipts on behalf of your users. WhatsApp follows the 24-hour customer > service window described above. > [!NOTE] > > **Your user must respond to an Instagram conversation within 7 days of the last message the > correspondent sent.** If the correspondent has not sent a message in 7 days the conversation is > considered inactive and cannot be responded to. > [!NOTE] > > **Comment-triggered automations use a different 7-day window.** A [`comment_keyword` automation](/docs/apis/automations/overview) replies to a *comment* via a private reply, whose 7-day window is anchored to the comment (not to a prior message), so no existing conversation is required. Delivery is still subject to the recipient's Message requests setting — see [Automation DM Sent but Not Delivered](/docs/help-center/technical-support/automation-dm-sent-but-not-delivered). ### Message History Retrieval for Facebook and Instagram When you first enable messaging for a user profile with a linked Facebook or Instagram, there are important limitations on accessing conversation history: #### Initial Connection Limitations
    • Facebook and Instagram only allow retrieval of the **last 20 messages** from existing conversations when you first link an account for messaging
    • If a conversation has more than 20 messages, only the most recent 20 will be accessible initially
    #### After Initial Setup
    • Once messaging is enabled, **all new messages** sent and received will be fully accessible
    • Ayrshare maintains complete conversation history going forward
    • For example: If you initially retrieve 20 messages, then 100 new messages are exchanged, you'll be able to access all 120 messages total
    This limitation only applies to historical messages that existed before enabling messaging - all future conversation activity for Facebook and Instagram will be fully preserved and accessible. ### Conversation Limit & Pricing
    • Each Ayrshare User Profile can have up to **1,000 active conversations per billing cycle**. The active conversation count is the sum across all social networks. Each User Profile has its own count, including messages sent through the API with that profile's `Profile-Key`.
    • **Premium plan**: Messaging is a flat **\$49 per month** add-on per account.
    • **Launch and Business plans**: Messaging is billed at **\$0.09 per active conversation** via Stripe metered billing. Volume-based discounts are available; contact your Ayrshare account representative for details.
    • A conversation is counted as active for the billing cycle when you send a message to a recipient and the social network accepts it. Inbound messages from a recipient do not, on their own, mark a conversation as active.
    • Messages Ayrshare sends for you automatically count the same as messages you send yourself. This includes [auto responses](/docs/apis/messages/set-auto-response) and the `send_dm` action in [Automations](/docs/apis/automations/overview).
    • If the 1,000 conversation limit has been reached, you can still receive messages and keep replying in conversations that are already active this billing cycle, but you cannot message a new recipient until the start of the next billing cycle. This applies to auto responses and automation DMs too, with one exception: an automation DM triggered by a comment is still sent, and still counted (see [Automations](/docs/apis/automations/overview#rate-limits-and-caps)).
    • The number of messages, sent or received, in an active conversation is unlimited.
    • If you need to increase the conversation limit, please contact your Ayrshare account representative.
    You can see the current conversation count with the [user endpoint](/docs/apis/user/overview). ### Messaging Control Issues on Facebook and Instagram Meta (Facebook and Instagram) uses a "thread control" system for messaging conversations. This means only one app can send messages in a conversation at a time, while other apps can only receive messages. #### What Does This Mean? If you see the error **"Message failed to send because another app is controlling this thread now"**, it means: * A different third-party app currently controls that conversation * Ayrshare can only receive messages in that conversation * Ayrshare cannot send messages until it regains control #### How to Fix This Issue To allow Ayrshare to send messages again, you need to remove the third-party app that's controlling the conversation. Please note that Meta (Facebook and Instagram) may update the process for removing third-party apps, so use the following steps as a general guideline. **For Facebook Pages:** 1. Go to [Facebook.com](https://facebook.com) and log into your account 2. Click the menu (☰) in the top right → **Settings & Privacy** → **Settings** 3. In the left sidebar, click **Apps and Websites** 4. Find the third-party app that's controlling your messaging 5. Click **Remove** next to that app 6. Confirm the removal **For Instagram Business Accounts:** 1. Open the Instagram app on your phone 2. Go to your **Profile** → tap the menu (☰) → **Settings and Privacy** 3. Tap **Apps and Websites** 4. Find and remove the third-party app **Alternative for Instagram:** 1. Go to [business.facebook.com](https://business.facebook.com) 2. Navigate to **Business Settings** 3. Under **Accounts**, click **Instagram Accounts** 4. Select your Instagram account 5. Check for connected apps and remove the problematic one #### After Removal Once you've removed the third-party app:
    • Thread control will automatically return to Ayrshare
    • You should be able to send messages normally again
    • No further action is required on your part
    If you continue to experience issues after removing the app, please contact our support team. ## Message WebHooks See [Messages Webhooks](/docs/apis/webhooks/actions#messages-action) to automatically receive messages, read receipts, or reactions. # Send Message Source: https://www.ayrshare.com/docs/apis/messages/send-message POST /messages/:platform Send a direct message on Facebook, Instagram, X/Twitter, or WhatsApp with text or mediaUrls; reactions are WhatsApp only. Active conversation pricing applies. > [!NOTE] > > **Targeting X/Twitter?** Starting March 31, 2026, all X operations require your own API credentials. After linking X via OAuth, include these 2 headers in your request: > > `X-Twitter-OAuth1-Api-Key` — Your API Key (Consumer Key) > > `X-Twitter-OAuth1-Api-Secret` — Your API Key Secret (Consumer Secret) > > **One-time setup per Ayrshare account.** You create one X Developer App and reuse the same API Key and Secret across every sub-profile / end-user you link. You do *not* create a new app per customer. > > Not linked yet? See the [full setup guide](/docs/dashboard/connect-social-accounts/x-twitter-byo-keys) to connect your X account. > > Your keys are never logged or stored by Ayrshare. Send a new direct message to a recipient. > [!NOTE] > > **Pricing & Limits:** Each User Profile can have up to **1,000 active > conversations per billing cycle** across all social networks. Pricing depends > on your plan: **Premium** plans pay a flat **\$49 per month** add-on, while > **Launch** and **Business** plans are billed at **\$0.09 per active > conversation**. A conversation is counted as > active when you send a message to a recipient during the billing cycle; > inbound messages alone do not count. The number of messages within an active > conversation is unlimited.
    • You can send an emoji as part of the `message` text.
    • Facebook and Instagram `mediaUrls` must end in a known extension. Including query parameters will cause the media URL to fail.
    > [!NOTE] > > **WhatsApp 24-hour customer service window.** WhatsApp Business only permits free-form > outbound messages within **24 hours** of the most recent message from the correspondent. After > that window, message sends will be rejected by Meta until the correspondent messages again. > Templated messages outside the 24-hour window are not currently supported through this endpoint. ## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` **Profile-Key** `string` [Profile Key](/docs/apis/overview#profile-key-format) of a User Profile. Format: `Profile-Key: PROFILE_KEY` ## Path Parameters **platform** `string` *required* The platform to send the message: `facebook`, `instagram`, `twitter`, `whatsapp` ## Body Parameters **recipientId** `string` The ID of the message recipient. Required for Facebook, Instagram, and WhatsApp. For X/Twitter, send `recipientId` or `conversationId`.
    • For **Facebook**, **Instagram**, and **X/Twitter** this is the platform-specific user ID (PSID, IGSID, or user ID).
    • For **WhatsApp** this is the recipient's phone number in E.164 digits-only format (for example `14155551234`, not `+1 (415) 555-1234`).
    **message** `string` The message to send to the recipient. Required unless `mediaUrls` or `reaction` is sent. Must be left out when sending a `reaction`.
    • Facebook, Instagram, and WhatsApp `message` may be an empty string or omitted entirely when `mediaUrls` is provided.
    • X requires a message of at least one character even if a `mediaUrls` is provided.
    **mediaUrls** `array` Array of media URLs for attaching images, video, audio (including voice messages), or documents.
    • URLs of media items should end in the file extension without additional parameters appended.
    • Facebook and Instagram support multiple media URLs.
    • X only accepts a single media URL.
    • Voice messages are supported on Facebook and Instagram as an `aac` or `wav` file.
    • **WhatsApp** accepts multiple media URLs. Ayrshare fetches each URL and uses its response `Content-Type` to select the WhatsApp media category. Meta accepts:
      • **Image:** `.jpg`, `.jpeg`, `.png`
      • **Video:** `.mp4`, `.3gp`
      • **Audio:** `.aac`, `.amr`, `.mp3`, `.m4a`, `.ogg`
      • **Document:** `.txt`, `.pdf`, `.ppt`, `.pptx`, `.doc`, `.docx`, `.xls`, `.xlsx`
    **conversationId** `string` X/Twitter only. Send the message into an existing conversation instead of to a `recipientId`. Get the ID from [Get Messages](/docs/apis/messages/get-messages). **reaction** `object` WhatsApp only. React to a message instead of sending one. Do not send `message` or `mediaUrls` with a reaction.
    • `messageId`: Required. The ID of the WhatsApp message to react to.
    • `emoji`: Required. A single emoji. Send an empty string `""` to remove your reaction.
    ```bash cURL theme={"system"} curl \ -H "Authorization: Bearer API_KEY" \ -H 'Content-Type: application/json' \ -d '{"message": "What's up!", "recipientId": "283j839222"} \ -X POST https://api.ayrshare.com/api/messages/instagram ``` ```javascript JavaScript theme={"system"} const apiKey = 'API_KEY'; const url = 'https://api.ayrshare.com/api/messages/instagram'; const data = { message: "What's up!", recipientId: '283j839222', }; const headers = { 'Authorization': `Bearer ${apiKey}`, 'Content-Type': 'application/json', }; fetch(url, { method: 'POST', headers: headers, body: JSON.stringify(data), }) .then(response => response.json()) .then(data => { console.log('Response:', data); }) .catch(error => { console.error('Error:', error); }); ``` ```python Python theme={"system"} import requests api_key = 'API_KEY' url = 'https://api.ayrshare.com/api/messages/instagram' data = { 'message': "What's up!", 'recipientId': '283j839222', } headers = { 'Authorization': f'Bearer {api_key}', 'Content-Type': 'application/json', } response = requests.post(url, json=data, headers=headers) print('Response:', response.json()) ``` ```php PHP theme={"system"} "What's up!", 'recipientId' => '283j839222', ]; $headers = [ 'Authorization: Bearer ' . $apiKey, 'Content-Type: application/json', ]; $ch = curl_init($url); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data)); curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $response = curl_exec($ch); if ($response === false) { $error = curl_error($ch); echo 'Error: ' . $error; } else { $responseData = json_decode($response, true); print_r($responseData); } curl_close($ch); ?> ``` ```csharp C# theme={"system"} using System; using System.Net.Http; using System.Net.Http.Headers; using System.Text; using System.Text.Json; using System.Threading.Tasks; class Program { static async Task Main() { string apiKey = "API_KEY"; string url = "https://api.ayrshare.com/api/messages/instagram"; var data = new { message = "What's up!", recipientId = "283j839222" }; var jsonData = JsonSerializer.Serialize(data); using var client = new HttpClient(); client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", apiKey); client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); var content = new StringContent(jsonData, Encoding.UTF8, "application/json"); try { var response = await client.PostAsync(url, content); response.EnsureSuccessStatusCode(); var responseBody = await response.Content.ReadAsStringAsync(); var responseData = JsonSerializer.Deserialize(responseBody); Console.WriteLine("Response:"); Console.WriteLine(responseData); } catch (HttpRequestException ex) { Console.WriteLine($"Error: {ex.Message}"); } } } ``` ```java Java theme={"system"} import java.io.IOException; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; public class Main { public static void main(String[] args) { String apiKey = "API_KEY"; String url = "https://api.ayrshare.com/api/messages/instagram"; String data = "{\"message\": \"What's up!\", \"recipientId\": \"283j839222\"}"; HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(url)) .header("Authorization", "Bearer " + apiKey) .header("Content-Type", "application/json") .POST(HttpRequest.BodyPublishers.ofString(data)) .build(); try { HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); if (response.statusCode() == 200) { String responseBody = response.body(); System.out.println("Response:"); System.out.println(responseBody); } else { System.out.println("Request failed. Status code: " + response.statusCode()); } } catch (IOException | InterruptedException e) { System.out.println("Error: " + e.getMessage()); } } } ``` ```json 200: Success theme={"system"} { // single text message "status": "success", "recipientId": "72706337063589124", "messageId": "aWdfZAG1faXRlbToxOkl", "message": "What is up?" } ``` ```json 400: Error theme={"system"} { // single text message "action": "messages", "status": "error", "code": 363, "message": "The recipient ID was not found. Please confirm the recipient ID is correct." } ``` ```json 200: Media Urls theme={"system"} { // single image "status": "success", "recipientId": "761943", "messageId": "m_EgvfBrgjaM", "type": "image", "mediaUrl": "https://img.ayrshare.com/012/gb.jpg" } ``` ```json 400: Media Urls theme={"system"} { // text and image message "action": "send", "status": "error", "code": 337, "message": "Error sending message.", "messages": [ { "recipientId": "727063370635", "messageId": "aWdfZAG1faXRlbT", "message": "What a great day" }, { "action": "messages", "status": "error", "code": 365, "message": "An error occurred sending the message attachment. Please verify the attachment is still available and try again." } ] } ``` ```json 200: WhatsApp Text theme={"system"} { "status": "success", "messagingProduct": "whatsapp", "contacts": [ { "input": "14155551234", "waId": "14155551234" } ], "recipientId": "14155551234", "messageId": "wamid.HBgLMTQxNTU1NTEyMzQVAgARGBI4OUYxRkExNzE0M0EwQTYwM0EA", "message": "Thanks — your order has shipped." } ``` ```json 200: WhatsApp Text + Media theme={"system"} { "status": "success", "messages": [ { "messagingProduct": "whatsapp", "recipientId": "14155551234", "messageId": "wamid.HBgLMTQxNTU1NTEyMzQVAgARGBI3MEM3Q0NDQjlGRTUzMjJBNEUA", "message": "Here's the shipping label you requested." }, { "messagingProduct": "whatsapp", "recipientId": "14155551234", "messageId": "wamid.HBgLMTQxNTU1NTEyMzQVAgARGBJDODc3NEJBRTdGN0NGNkVCMEMA", "type": "document", "mediaUrl": "https://example.com/label.pdf" } ] } ``` # Set Auto Response Source: https://www.ayrshare.com/docs/apis/messages/set-auto-response POST /messages/autoresponse Send an auto response to Facebook, Instagram, and X/Twitter messages, with a custom message and wait time, when your support desk is unavailable. Automatically send message auto responses to the correspondent. This is useful if your customer service support desk is not currently available. If active, the auto response is used for Facebook, Instagram, and X/Twitter messages for a given User Profile. On X/Twitter, it is sent only when new messages are retrieved with your X API keys, through [Get Messages](/docs/apis/messages/get-messages) or the Messages page of the Ayrshare dashboard, and each retrieval sends at most one auto response, to the sender of the newest message. WhatsApp messages do not get auto responses. > [!NOTE] > > An auto response is a message sent on your behalf, so it counts toward the User Profile's monthly [conversation limit](/docs/apis/messages/overview#conversation-limit-&-pricing) the same as a message you send yourself. When the limit is reached, auto responses to new recipients are not sent until the next billing cycle. ## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` **Profile-Key** `string` [Profile Key](/docs/apis/overview#profile-key-format) of a User Profile. Format: `Profile-Key: PROFILE_KEY` ## Body Parameters **autoResponseActive** `boolean` (default: false) Whether the auto response is active. **autoResponseWaitSeconds** `number` (default: 86400) The number of seconds to wait before sending the auto response again to the correspondent. Default is 86,400 seconds (24 hours). **autoResponseMessage** `string` The auto response message. Default: "Thank you for contacting us. A customer care agent will get back to you soon." Send an empty "" string to reset the message to the default. ```bash cURL theme={"system"} curl \ -H "Authorization: Bearer API_KEY" \ -H 'Content-Type: application/json' \ -d '{"autoResponseActive": true, "autoResponseWaitSeconds": 30, "autoResponseMessage": "Howdy!"' \ -X POST https://api.ayrshare.com/api/messages/autoresponse ``` ```javascript JavaScript theme={"system"} const url = 'https://api.ayrshare.com/api/messages/autoresponse'; const options = { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer API_KEY' }, body: JSON.stringify({ 'autoResponseActive': true, 'autoResponseWaitSeconds': 30, 'autoResponseMessage': 'Howdy!' }) }; fetch(url, options) .then(response => response.json()) .then(data => console.log(data)) .catch(error => console.error('Error:', error)); ``` ```python Python theme={"system"} import json import requests url = 'https://api.ayrshare.com/api/messages/autoresponse' headers = { 'Content-Type': 'application/json', 'Authorization': 'Bearer API_KEY' } data = { 'autoResponseActive': True, 'autoResponseWaitSeconds': 30, 'autoResponseMessage': 'Howdy!' } response = requests.post(url, headers=headers, data=json.dumps(data)) try: response.raise_for_status() data = response.json() print(data) except requests.exceptions.RequestException as e: print('Error:', e) ``` ```php PHP theme={"system"} true, 'autoResponseWaitSeconds' => 30, 'autoResponseMessage' => 'Howdy!' ]; $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data)); $response = curl_exec($ch); if (curl_errno($ch)) { echo 'Error: ' . curl_error($ch); } else { $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); if ($httpCode >= 400) { echo 'Error: HTTP ' . $httpCode; } else { $data = json_decode($response, true); print_r($data); } } curl_close($ch); ``` ```csharp C# theme={"system"} using System; using System.Collections.Generic; using System.Net.Http; using System.Text; using System.Text.Json; using System.Threading.Tasks; class Program { static async Task Main(string[] args) { string url = "https://api.ayrshare.com/api/messages/autoresponse"; var headers = new Dictionary { {"Content-Type", "application/json"}, {"Authorization", "Bearer API_KEY"} }; var data = new Dictionary { {"autoResponseActive", true}, {"autoResponseWaitSeconds", 30}, {"autoResponseMessage", "Howdy!"} }; using (var client = new HttpClient()) { var jsonData = JsonSerializer.Serialize(data); var content = new StringContent(jsonData, Encoding.UTF8, "application/json"); foreach (var header in headers) { client.DefaultRequestHeaders.Add(header.Key, header.Value); } var response = await client.PostAsync(url, content); try { response.EnsureSuccessStatusCode(); string responseBody = await response.Content.ReadAsStringAsync(); var responseData = JsonSerializer.Deserialize>(responseBody); Console.WriteLine(JsonSerializer.Serialize(responseData, new JsonSerializerOptions { WriteIndented = true })); } catch (HttpRequestException e) { Console.WriteLine($"Error: {e.Message}"); } } } } ``` ```java Java theme={"system"} import java.io.IOException; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.util.HashMap; import java.util.Map; public class Main { public static void main(String[] args) { String url = "https://api.ayrshare.com/api/messages/autoresponse"; Map headers = new HashMap<>(); headers.put("Content-Type", "application/json"); headers.put("Authorization", "Bearer API_KEY"); Map data = new HashMap<>(); data.put("autoResponseActive", true); data.put("autoResponseWaitSeconds", 30); data.put("autoResponseMessage", "Howdy!"); HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(url)) .headers(headers.entrySet().stream() .map(entry -> entry.getKey() + ": " + entry.getValue()) .toArray(String[]::new)) .POST(HttpRequest.BodyPublishers.ofString(getJsonString(data))) .build(); try { HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); if (response.statusCode() >= 400) { System.out.println("Error: HTTP " + response.statusCode()); } else { System.out.println(response.body()); } } catch (IOException | InterruptedException e) { System.out.println("Error: " + e.getMessage()); } } private static String getJsonString(Map data) { StringBuilder jsonBuilder = new StringBuilder(); jsonBuilder.append("{"); for (Map.Entry entry : data.entrySet()) { jsonBuilder.append("\"").append(entry.getKey()).append("\":"); if (entry.getValue() instanceof String) { jsonBuilder.append("\"").append(entry.getValue()).append("\""); } else { jsonBuilder.append(entry.getValue()); } jsonBuilder.append(","); } if (jsonBuilder.length() > 1) { jsonBuilder.setLength(jsonBuilder.length() - 1); } jsonBuilder.append("}"); return jsonBuilder.toString(); } } ``` ```json 200: Success theme={"system"} { "status": "success", "updated": { "autoResponseActive": true, "autoResponseMessage": "Howdy!", "autoResponseWaitSeconds": 30 } } ``` ```json 400: Error theme={"system"} { "action": "request", "status": "error", "code": 101, "message": "Missing or incorrect parameters. Please verify with the docs. .../ayrshare.com/rest-api/endpoints" } ``` # Update Messages Source: https://www.ayrshare.com/docs/apis/messages/update-messages PUT /messages/:platform/:conversationId Update the status of a Facebook, Instagram, X/Twitter, or WhatsApp conversation to active or archived when it is no longer active or relevant to your team. Update the status of a conversation to active or archived. You may want to archive conversations if they are no longer active or relevant. ## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` **Profile-Key** `string` [Profile Key](/docs/apis/overview#profile-key-format) of a User Profile. Format: `Profile-Key: PROFILE_KEY` ## **Path Parameters** **platform** `string` *required* The platform for status update: `facebook`, `instagram`, `twitter`, `whatsapp` **conversationId** `string` *required* The ID of the conversation to action. For WhatsApp this is the correspondent's phone number (digits only, E.164 without the leading `+`). ## Body Parameters **status** `string` *required* Values: `active` or `archived`. ```bash cURL theme={"system"} curl \ -H "Authorization: Bearer API_KEY" \ -H 'Content-Type: application/json' \ -d '{"status": "archived"}' \ -X PUT https://api.ayrshare.com/api/messages/instagram/aWdfZMTpIyzQw ``` ```javascript JavaScript theme={"system"} const url = 'https://api.ayrshare.com/api/messages/instagram/aWdfZMTpIyzQw'; const options = { method: 'PUT', headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer API_KEY' }, body: JSON.stringify({ 'status': 'archived' }) }; fetch(url, options) .then(response => response.json()) .then(data => console.log(data)) .catch(error => console.error('Error:', error)); ``` ```python Python theme={"system"} import json import requests url = 'https://api.ayrshare.com/api/messages/instagram/aWdfZMTpIyzQw' headers = { 'Content-Type': 'application/json', 'Authorization': 'Bearer API_KEY' } data = { 'status': 'archived' } response = requests.put(url, headers=headers, data=json.dumps(data)) try: response.raise_for_status() data = response.json() print(data) except requests.exceptions.RequestException as e: print('Error:', e) ``` ```php PHP theme={"system"} 'archived' ]; $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT'); curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data)); $response = curl_exec($ch); if (curl_errno($ch)) { echo 'Error: ' . curl_error($ch); } else { $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); if ($httpCode >= ``` ```csharp C# theme={"system"} using System; using System.Collections.Generic; using System.Net.Http; using System.Text; using System.Text.Json; using System.Threading.Tasks; class Program { static async Task Main(string[] args) { string url = "https://api.ayrshare.com/api/messages/instagram/aWdfZMTpIyzQw"; var headers = new Dictionary { {"Content-Type", "application/json"}, {"Authorization", "Bearer API_KEY"} }; var data = new Dictionary { {"status", "archived"} }; using (var client = new HttpClient()) { var jsonData = JsonSerializer.Serialize(data); var content = new StringContent(jsonData, Encoding.UTF8, "application/json"); foreach (var header in headers) { client.DefaultRequestHeaders.Add(header.Key, header.Value); } var response = await client.PutAsync(url, content); try { response.EnsureSuccessStatusCode(); string responseBody = await response.Content.ReadAsStringAsync(); var responseData = JsonSerializer.Deserialize>(responseBody); Console.WriteLine(JsonSerializer.Serialize(responseData, new JsonSerializerOptions { WriteIndented = true })); } catch (HttpRequestException e) { Console.WriteLine($"Error: {e.Message}"); } } } } ``` ```java Java theme={"system"} import java.io.IOException; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.Map; public class Main { public static void main(String[] args) { String url = "https://api.ayrshare.com/api/messages/instagram/aWdfZMTpIyzQw"; Map headers = new HashMap<>(); headers.put("Content-Type", "application/json"); headers.put("Authorization", "Bearer API_KEY"); Map data = new HashMap<>(); data.put("status", "archived"); HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(url)) .headers(headers.entrySet().stream() .map(entry -> entry.getKey() + ": " + entry.getValue()) .toArray(String[]::new)) .PUT(HttpRequest.BodyPublishers.ofString(getJsonString(data))) .build(); try { HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); if (response.statusCode() >= 400) { System.out.println("Error: HTTP " + response.statusCode()); } else { System.out.println(response.body()); } } catch (IOException | InterruptedException e) { System.out.println("Error: " + e.getMessage()); } } private static String getJsonString(Map data) { StringBuilder jsonBuilder = new StringBuilder(); jsonBuilder.append("{"); for (Map.Entry entry : data.entrySet()) { jsonBuilder.append("\"").append(entry.getKey()).append("\":\"").append(entry.getValue()).append("\","); } if (jsonBuilder.length() > 1) { jsonBuilder.setLength(jsonBuilder.length() - 1); } jsonBuilder.append("}"); return jsonBuilder.toString(); } } ``` ```json 200: Success theme={"system"} { "status": "success" } ``` ```json 400: Missing Status theme={"system"} { "action": "request", "status": "error", "code": 101, "message": "Missing or incorrect parameters. Please verify with the docs. .../ayrshare.com/rest-api/endpoints" } ``` # Bulk Post Source: https://www.ayrshare.com/docs/apis/post/bulk-post POST /post/bulk Schedule many posts at once by uploading a CSV file as multipart/form-data. Covers the required CSV fields, duplicate post rules, and a CSV template. > [!NOTE] > > **Targeting X/Twitter?** Starting March 31, 2026, all X operations require your own API credentials. After linking X via OAuth, include these 2 headers in your request: > > `X-Twitter-OAuth1-Api-Key` — Your API Key (Consumer Key) > > `X-Twitter-OAuth1-Api-Secret` — Your API Key Secret (Consumer Secret) > > **One-time setup per Ayrshare account.** You create one X Developer App and reuse the same API Key and Secret across every sub-profile / end-user you link. You do *not* create a new app per customer. > > Not linked yet? See the [full setup guide](/docs/dashboard/connect-social-accounts/x-twitter-byo-keys) to connect your X account. > > Your keys are never logged or stored by Ayrshare. Bulk schedule posts with CSV (Comma Separated Values) file of posts data. Content-Type must be `multipart/form-data`. > [!WARNING] > > We recommend using the direct [Post endpoint](/docs/apis/post/post) instead of this bulk method for > scheduling posts. The direct endpoint provides a more comprehensive feature set and easier > debugging capabilities. ## Header Parameters **Authorization** `string` *required* Format: `Authorization: Bearer API_KEY`. See [API overview](/docs/apis/overview#authorization) for more information. **Profile-Key** `string` Profile Key of a user profile. **Content-Type** `string` *required* `Content-Type: multipart/form-data` ## Body Parameters **file** `object` Multipart form-data CSV file of scheduled posts. See below for [CSV template](#csv-template). ## Request Examples A multipart form-data containing a CSV file of posts will schedule them for a future date. The CSV file contains the following fields (template below) and are required:
    • `post`: The post text.
    • `platforms`: Comma separated list of platforms, e.g. "twitter, facebook, instagram".
    • `mediaUrls`: URL of media, such as an image or video to include in the post.
    • `scheduleDate`: Datetime to schedule the post in UTC format. For example, use format `YYYY-MM-DDThh:mm:ssZ` and send as `2026-07-08T12:30:00Z`. Please see [utctime](https://www.utctime.net/) for more examples.
    > [!WARNING] > > Don't schedule duplicate posts within 24 hours of each other. > > Each post is checked against a 48-hour window centered on its own `scheduleDate` — 24 hours before through 24 hours after. If another post with identical or near-identical text falls inside that window, the post is rejected with [error 137](/docs/errors/errors-ayrshare#duplicate-content-errors). The window looks forward as well as back, so a row can be rejected because of a row scheduled after it. The check runs when a post is submitted or scheduled, not only when it is about to publish. > This is to protect your account at the networks; they can suspend or shadow-ban accounts with frequent duplicate posts. ## CSV Template Download the template and save as a .csv file. [Ayrshare CSV Template](https://img.ayrshare.com/012/Ayrshare_CSV_Template.csv) ```bash cURL theme={"system"} curl \ -H "Authorization: Bearer API_KEY" \ -H 'Content-Type: multipart/form-data' \ -F 'file=@"./Ayrshare CSV Template.csv"' \ -X POST https://api.ayrshare.com/api/post/bulk ``` ```javascript JavaScript theme={"system"} const API_KEY = "API_KEY"; const FormData = require("form-data"); const fs = require("fs"); const formData = new FormData(); formData.append("file", fs.createReadStream("./Ayrshare CSV Template.csv")); fetch("https://api.ayrshare.com/api/post/bulk", { method: "POST", headers: { Authorization: `Bearer ${API_KEY}`, ...formData.getHeaders() }, body: formData }) .then((res) => res.json()) .then((data) => { console.log(JSON.stringify(data)); }) .catch((error) => { console.log(error); }); ``` ```python Python theme={"system"} import requests API_KEY = "API_KEY" # Open the CSV file in binary read mode with open('./Ayrshare CSV Template.csv', 'rb') as file: # Prepare the files dictionary for the multipart/form-data request files = {'file': file} # Set up the authorization header headers = {'Authorization': f'Bearer {API_KEY}'} try: # Make the POST request to the API response = requests.post( 'https://api.ayrshare.com/api/post/bulk', headers=headers, files=files ) # Parse and print the JSON response data = response.json() print(data) except Exception as e: print(f"Error: {e}") ``` ```javascript 200: OK Example with two scheduled posts. theme={"system"} { "status": "success", "posts": [ { "status": "scheduled", "scheduleDate": "4/6/21 12:50", "id": "X3uTExuEJhyM3u8wCRsA", "post": "A great post" }, { "status": "scheduled", "scheduleDate": "4/6/21 13:00", "id": "8RGrekuxMnVa7lVnARFm", "post": "An even better post" } ] } ``` # Copy a Post Source: https://www.ayrshare.com/docs/apis/post/copy-post POST /post/copy Copy an existing post to new social media platforms with the same content, choose target platforms, set a schedule date, and override platform options. > [!NOTE] > > **Targeting X/Twitter?** Starting March 31, 2026, all X operations require your own API credentials. After linking X via OAuth, include these 2 headers in your request: > > `X-Twitter-OAuth1-Api-Key` — Your API Key (Consumer Key) > > `X-Twitter-OAuth1-Api-Secret` — Your API Key Secret (Consumer Secret) > > **One-time setup per Ayrshare account.** You create one X Developer App and reuse the same API Key and Secret across every sub-profile / end-user you link. You do *not* create a new app per customer. > > Not linked yet? See the [full setup guide](/docs/dashboard/connect-social-accounts/x-twitter-byo-keys) to connect your X account. > > Your keys are never logged or stored by Ayrshare. Copy an existing post to new social media platforms with the same content and settings. This endpoint allows you to reuse successful posts across different platforms or with different configurations. Please be sure to add your `API_KEY`, and the `PROFILE_KEY` if copying to a User Profile, in the [Authorization header](/docs/apis/overview#authorization). The API Key can be found in the Ayrshare Developer Dashboard under the API Key page. ## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` **Profile-Key** `string` [Profile Key](/docs/apis/overview#profile-key-format) of a User Profile. Format: `Profile-Key: PROFILE_KEY` ## Body Parameters **postId** `string` *required* The ID of the existing post to copy. This is the Ayrshare Post ID returned from the original `/post` endpoint. **platforms** `array` *required* Social media platforms to post the copied content to. Accepts an array of Strings with values: `bluesky`, `facebook`, `gmb`, `instagram`, `linkedin`, `pinterest`, `reddit`, `snapchat`, `telegram`, `threads`, `tiktok`, `twitter`, or `youtube`. Please note: use `facebook` for Facebook Pages, and `gmb` for Google Business Profile. The platforms specified here will override the original post's platforms - you must explicitly choose which platforms to copy to. **scheduleDate** `string` The datetime to schedule the copied post. Accepts a UTC date time. For example, use format `YYYY-MM-DDThh:mm:ssZ` and send as `2026-07-08T12:30:00Z`. Please see [utctime](https://www.utctime.net/) for more examples. If not provided, the post will be published immediately. **blueSkyOptions** `object` Override the original post's Bluesky settings. See [Bluesky details](/docs/apis/post/social-networks/bluesky). **faceBookOptions** `object` Override the original post's Facebook settings. See [Facebook details](/docs/apis/post/social-networks/facebook). **gmbOptions** `object` Override the original post's Google Business Profile settings. See [Google Business Profile details](/docs/apis/post/social-networks/google). **instagramOptions** `object` Override the original post's Instagram settings. See [Instagram details](/docs/apis/post/social-networks/instagram). **linkedInOptions** `object` Override the original post's LinkedIn settings. See [LinkedIn details](/docs/apis/post/social-networks/linkedin). **pinterestOptions** `object` Override the original post's Pinterest settings. See [Pinterest details](/docs/apis/post/social-networks/pinterest). **redditOptions** `object` Override the original post's Reddit settings. See [Reddit details](/docs/apis/post/social-networks/reddit). **snapChatOptions** `object` Override the original post's Snapchat settings. See [Snapchat details](/docs/apis/post/social-networks/snapchat). **telegramOptions** `object` Override the original post's Telegram settings. See [Telegram details](/docs/apis/post/social-networks/telegram). **threadsOptions** `object` Override the original post's Threads settings. See [Threads details](/docs/apis/post/social-networks/threads). **tikTokOptions** `object` Override the original post's TikTok settings. See [TikTok details](/docs/apis/post/social-networks/tiktok). **twitterOptions** `object` Override the original post's X/Twitter settings. See [X/Twitter details](/docs/apis/post/social-networks/x-twitter). **youTubeOptions** `object` Override the original post's YouTube settings. See [YouTube details](/docs/apis/post/social-networks/youtube). **shortenLinks** `boolean` Override the original post's link shortening setting. [Max Pack required](/docs/additional/maxpack) for link shortening. **autoHashtag** `object` Override the original post's auto hashtag settings. See [auto hashtags](/docs/apis/post/overview#auto-hashtags) for details. **disableComments** `boolean` Override the original post's comment settings. Only available for Instagram, LinkedIn, and TikTok. **firstComment** `object` Override the original post's first comment settings. See [first comment](/docs/apis/post/overview#first-comment) for details. **notes** `string` Add new notes to the copied post. These will replace any notes from the original post. Notes are for reference only and do not affect the post content. ```bash cURL theme={"system"} curl \ -H "Authorization: Bearer API_KEY" \ -H 'Content-Type: application/json' \ -d '{ "postId": "RhrbDtYh7hdSMc67zC8H", "platforms": ["twitter", "facebook", "linkedin"], "scheduleDate": "2026-07-08T12:30:00Z" }' \ -X POST https://api.ayrshare.com/api/post/copy ``` ```javascript JavaScript theme={"system"} const API_KEY = "API_KEY"; fetch("https://api.ayrshare.com/api/post/copy", { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${API_KEY}` }, body: JSON.stringify({ postId: "RhrbDtYh7hdSMc67zC8H", // required platforms: ["twitter", "facebook", "linkedin"], // required scheduleDate: "2026-07-08T12:30:00Z", // optional faceBookOptions: { link: "https://example.com/new-link" } }) }) .then((res) => res.json()) .then((json) => console.log(json)) .catch(console.error); ``` ```python Python theme={"system"} import requests payload = { 'postId': 'RhrbDtYh7hdSMc67zC8H', 'platforms': ['twitter', 'facebook', 'linkedin'], 'scheduleDate': '2026-07-08T12:30:00Z', 'faceBookOptions': { 'link': 'https://example.com/new-link' } } headers = { 'Content-Type': 'application/json', 'Authorization': 'Bearer API_KEY' } r = requests.post('https://api.ayrshare.com/api/post/copy', json=payload, headers=headers) print(r.json()) ``` ```php PHP theme={"system"} "RhrbDtYh7hdSMc67zC8H", "platforms" => ["twitter", "facebook", "linkedin"], "scheduleDate" => "2026-07-08T12:30:00Z", "faceBookOptions" => [ "link" => "https://example.com/new-link" ] ]; curl_setopt_array($curl, [ CURLOPT_URL => 'https://api.ayrshare.com/api/post/copy', CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_POSTFIELDS => json_encode($data), CURLOPT_HTTPHEADER => [ 'Authorization: Bearer API_KEY', 'Content-Type: application/json' ], ]); $response = curl_exec($curl); curl_close($curl); echo $response; ``` ```go Go theme={"system"} package main import ( "bytes" "encoding/json" "log" "net/http" ) func main() { message := map[string]interface{}{ "postId": "RhrbDtYh7hdSMc67zC8H", "platforms": []string{"twitter", "facebook", "linkedin"}, "scheduleDate": "2026-07-08T12:30:00Z", "faceBookOptions": map[string]interface{}{ "link": "https://example.com/new-link", }, } bytesRepresentation, err := json.Marshal(message) if err != nil { log.Fatalln(err) } req, _ := http.NewRequest("POST", "https://api.ayrshare.com/api/post/copy", bytes.NewBuffer(bytesRepresentation)) req.Header.Add("Content-Type", "application/json; charset=UTF-8") req.Header.Add("Authorization", "Bearer API_KEY") res, err := http.DefaultClient.Do(req) if err != nil { log.Fatal("Error:", err) } res.Body.Close() } ``` ```csharp C# theme={"system"} using System; using System.Net.Http; using System.Text; using System.Threading.Tasks; namespace PostCopyRequest_csharp { class PostCopy { private static readonly HttpClient client = new HttpClient(); static async Task Main(string[] args) { string API_KEY = "API_KEY"; string url = "https://api.ayrshare.com/api/post/copy"; client.DefaultRequestHeaders.Add("Authorization", "Bearer " + API_KEY); string json = "{\"postId\" : \"RhrbDtYh7hdSMc67zC8H\"," + "\"platforms\" : [ \"twitter\", \"facebook\", \"linkedin\" ]," + "\"scheduleDate\" : \"2026-07-08T12:30:00Z\"}"; var content = new StringContent(json, Encoding.UTF8, "application/json"); try { HttpResponseMessage response = await client.PostAsync(url, content); response.EnsureSuccessStatusCode(); string responseBody = await response.Content.ReadAsStringAsync(); Console.WriteLine(responseBody); } catch (HttpRequestException e) { Console.WriteLine($"Error: {e.Message}"); } } } } ``` ```ruby Ruby theme={"system"} require 'httparty' res = HTTParty.post("https://api.ayrshare.com/api/post/copy", headers: {Authorization: "Bearer API_KEY"}, body: { postId: "RhrbDtYh7hdSMc67zC8H", platforms: ['twitter', 'facebook', 'linkedin'], scheduleDate: '2026-07-08T12:30:00Z', faceBookOptions: { link: 'https://example.com/new-link' } }).body puts res ``` ```json 200: Success - Immediate Post theme={"system"} { "status": "success", "copiedFrom": "RhrbDtYh7hdSMc67zC8H", "originalPost": "Today is a great day!", "errors": [], "postIds": [ { "status": "success", "id": "1288899996423983106", "platform": "twitter", "postUrl": "https://x.com/handle/status/1288899996423983106" }, { "status": "success", "id": "104923907983682_108329000309743", "platform": "facebook", "postUrl": "https://www.facebook.com/104923907983682_108329000309743" }, { "status": "success", "id": "urn:li:share:7282181682126807042", "postUrl": "https://www.linkedin.com/feed/update/urn:li:share:7282181682126807042", "owner": "urn:li:organization:77682157", "platform": "linkedin" } ], "id": "KpwxFmNj3kgQLa92vE5T" } ``` ```json 200: Success - Scheduled Post theme={"system"} { "status": "scheduled", "scheduleDate": "2025-05-28T14:32:00Z", "id": "KpwxFmNj3kgQLa92vE5T", "refId": "7a8e2f15c94d73a6e2bf89dd4c847b9f2e3a6d18", "copiedFrom": "RhrbDtYh7hdSMc67zC8H", "originalPost": "Today is a great day!" } ``` ```json 400: Bad Request theme={"system"} { "status": "error", "errors": [ { "action": "post", "status": "error", "code": 156, "message": "Threads is not linked. Please confirm the linkage on the Social Accounts page in the dashboard. https://www.ayrshare.com/docs/dashboard/connect-social-accounts/overview", "platform": "threads" } ], "postIds": [], "id": "KpwxFmNj3kgQLa92vE5T", "refId": "7a8e2f15c94d73a6e2bf89dd4c847b9f2e3a6d18" } ``` ```json 400: Bad Request - Missing Platforms theme={"system"} { "status": "error", "code": 163, "message": "Missing, empty, or not valid platforms parameter. Please verify sending an array of valid platforms. https://www.ayrshare.com/docs/apis/post", "details": "platforms parameter is required for copying posts" } ``` ```json 400: Bad Request - Duplicate or Similar Content theme={"system"} { "status": "error", "errors": [ { "action": "post", "status": "error", "code": 137, "message": "Duplicate or similar content posted within the same two day period. The social networks prohibit duplicate content and ban accounts that do not comply. https://www.ayrshare.com/docs/help-center/technical-support/dealing-with-duplicate-posts#dealing-with-duplicate-posts", "details": "Duplicate found in user profile: \"Primary Profile\", post id: e5ELaDhKjhJBU1z6dbud, and refId: 5da3de47a8e3b1d8c2bd319350aa7d4e85d4ec74", "verifyCheck": true, "platform": "twitter" } ], "postIds": [], "id": "KpwxFmNj3kgQLa92vE5T", "refId": "7a8e2f15c94d73a6e2bf89dd4c847b9f2e3a6d18" } ``` # Delete a Post Source: https://www.ayrshare.com/docs/apis/post/delete-post DELETE /post Delete published or scheduled posts by Ayrshare Post ID, bulk delete several posts, or delete all pending scheduled posts for a User Profile at once. > [!NOTE] > > **Targeting X/Twitter?** Starting March 31, 2026, all X operations require your own API credentials. After linking X via OAuth, include these 2 headers in your request: > > `X-Twitter-OAuth1-Api-Key` — Your API Key (Consumer Key) > > `X-Twitter-OAuth1-Api-Secret` — Your API Key Secret (Consumer Secret) > > **One-time setup per Ayrshare account.** You create one X Developer App and reuse the same API Key and Secret across every sub-profile / end-user you link. You do *not* create a new app per customer. > > Not linked yet? See the [full setup guide](/docs/dashboard/connect-social-accounts/x-twitter-byo-keys) to connect your X account. > > Your keys are never logged or stored by Ayrshare. Delete a post using the post ID returned from the [/post](/docs/apis/post/post) endpoint. You can delete both published and scheduled posts, but there are some differences based on the social network `platform`:
    • Scheduled posts can be deleted for all social networks `platforms`.
    • Published posts can be deleted for all social networks `platforms` except for Instagram and TikTok. Instagram and TikTok do not provide API support for deleting published posts. If you need to delete a published post on these platforms, you must do so manually using their respective mobile apps.
    • Facebook does not permit deleting image stories via their API (video stories can be deleted). If you need to delete an image story, please go to the Facebook mobile app and perform the deletion manually.
    > [!NOTE] > > Instagram and TikTok do not support delete via their APIs. Please go to the > Instagram or TikTok mobile apps to delete the posts. > > Facebook does not support image story deletion via its API. Please go to the > Facebook mobile app to delete stories. ## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` **Profile-Key** `string` [Profile Key](/docs/apis/overview#profile-key-format) of a User Profile. Format: `Profile-Key: PROFILE_KEY` ## Body Parameters **id** `string` *required* Ayrshare Post ID of the post to delete. Not required if `bulk` or `deleteAllScheduled` parameter sent. If the post is scheduled and still `pending`, the scheduled posts will be deleted and not sent to the networks. If the post has already been sent to the networks, the post will be deleted from the networks. **bulk** `array` Array of Strings post Ids to bulk delete. Required if `id` or `deleteAllScheduled` parameter not sent. **deleteAllScheduled** `boolean` (default: false) If `true` will delete all scheduled posts still in a pending state, i.e. posts that have not yet been published to the networks, for the User Profile. The `id` parameter is ignored if set to `true`. If `bulk` is also sent, `bulk` is used and `deleteAllScheduled` is ignored. > [!TIP] > > This will delete all scheduled posts, so be sure to use this with caution. **markManualDeleted** `boolean` (default: false) If `true`, the post will mark as deleted in Ayrshare and Ayrshare will not try to delete the post at the networks. A common use case for this is when the post was manually deleted at the networks and you want to mark it as deleted in Ayrshare. Prior to marking the post as deleted, please ensure that the post was already manually deleted on the social networks. ```bash cURL theme={"system"} curl \ -H "Authorization: Bearer API Key" \ -H 'Content-Type: application/json' \ -d '{"id": "Post ID"}' \ -X DELETE https://api.ayrshare.com/api/post ``` ```javascript JavaScript theme={"system"} const API_KEY = "API_KEY"; const id = "Post ID"; fetch("https://api.ayrshare.com/api/post", { method: "DELETE", headers: { "Content-Type": "application/json", "Authorization": `Bearer ${API_KEY}` }, body: JSON.stringify({ id }), }) .then((res) => res.json()) .then((json) => console.log(json)) .catch(console.error); ``` ```python Python theme={"system"} import requests payload = {'id': 'Post ID'} headers = {'Content-Type': 'application/json', 'Authorization': 'Bearer API_KEY'} r = requests.delete('https://api.ayrshare.com/api/post', json=payload, headers=headers) print(r.json()) ``` ```php PHP theme={"system"} $postId]); $curl = curl_init($apiUrl); curl_setopt_array($curl, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_CUSTOMREQUEST => 'DELETE', CURLOPT_HTTPHEADER => $headers, CURLOPT_POSTFIELDS => $data ]); $response = curl_exec($curl); if ($response === false) { echo 'Curl error: ' . curl_error($curl); } else { echo json_encode(json_decode($response), JSON_PRETTY_PRINT); } curl_close($curl); ``` ```csharp C# theme={"system"} using System; using System.Net.Http; using System.Text; using System.Threading.Tasks; namespace DeletePOSTRequest_csharp { class Delete { private static readonly HttpClient client = new HttpClient(); static async Task Main(string[] args) { string API_KEY = "API_KEY"; string url = "https://api.ayrshare.com/api/post"; // Set up request headers client.DefaultRequestHeaders.Add("Authorization", "Bearer " + API_KEY); // Prepare JSON content string json = "{\"id\" : \"Post ID\"}"; var content = new StringContent(json, Encoding.UTF8, "application/json"); try { // Create and send DELETE request with content var request = new HttpRequestMessage { Method = HttpMethod.Delete, RequestUri = new Uri(url), Content = content }; HttpResponseMessage response = await client.SendAsync(request); response.EnsureSuccessStatusCode(); // Read response string responseBody = await response.Content.ReadAsStringAsync(); Console.WriteLine(responseBody); } catch (HttpRequestException e) { Console.WriteLine($"Error: {e.Message}"); } } } } ``` ```javascript 200: Success theme={"system"} { "twitter": { "action": "delete", "status": "success", "id": "1288900099663775749" // Twitter Social Post ID }, "facebook": { "action": "delete", "status": "success", "id": "104920007983682_108683297607743" // Facebook Social PostID }, "status": "success" } ``` ```json 200: Success with markManualDeleted theme={"system"} { "status": "success", "id": "p8Ee6xDPx8PRxsjtDXf3" // Ayrshare Post ID } ``` ```json 400: ID Not Found theme={"system"} { "action": "delete", "status": "error", "code": 114, "message": "Delete id not found.", "id": "IdpUOyihLEpIx0d0N9mI" // Ayrshare Post ID used for delete, comment, analytics, etc. } ``` ```json 400: Post Already Deleted theme={"system"} { "action": "delete", "status": "error", "code": 383, "message": "The post is already deleted.", "id": "p8Ee6xDPx8PRxsjtDXf3" // Ayrshare Post ID } ``` ```json 400: Mark Manual Deleted Error theme={"system"} { "action": "delete", "status": "error", "code": 382, "message": "The post was not manually deleted at the social network. By using the markManualDeleted option, you are responsible for deleting the post at the social network. Please delete the post and try again.", "id": "DD3fA2DC4qe2T48hSdgs", "markManualDeleted": true } ``` # Get a Post Source: https://www.ayrshare.com/docs/apis/post/get-post GET /post/:id Get the history of a post sent via Ayrshare by its Post ID, including its status, such as pending, success, or error, post parameters, and other details. > [!NOTE] > > **Targeting X/Twitter?** Starting March 31, 2026, all X operations require your own API credentials. After linking X via OAuth, include these 2 headers in your request: > > `X-Twitter-OAuth1-Api-Key` — Your API Key (Consumer Key) > > `X-Twitter-OAuth1-Api-Secret` — Your API Key Secret (Consumer Secret) > > **One-time setup per Ayrshare account.** You create one X Developer App and reuse the same API Key and Secret across every sub-profile / end-user you link. You do *not* create a new app per customer. > > Not linked yet? See the [full setup guide](/docs/dashboard/connect-social-accounts/x-twitter-byo-keys) to connect your X account. > > Your keys are never logged or stored by Ayrshare. Get the history for a specific posts sent via Ayrshare. Returns the status, post parameters, and other details. Replace `:id` with the Ayrshare Post ID. > [!NOTE] > > Call the [/history by id](/docs/apis/history/get-history-id) endpoint for the same data. ## Post Statuses The following statuses are returned for a post. | Status | Description | | :- | :- | | `awaiting approval` | Posts are waiting to be approved via the [approval workflow](/docs/apis/post/overview#approval-workflow). | | `deleted` | Post has been deleted. Note: deleted posts are only returned with the status query filter. Please see below. | | `error` | An error occurred with one or more social networks. | | `pending` | The post has not yet been processed. Typically a scheduled post. | | `success` | The post was successfully sent to all social networks. | > [!NOTE] > > See the [/history](/docs/apis/history/overview) endpoint for retrieving all posts, including > posts not sent via Ayrshare. ## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` **Profile-Key** `string` [Profile Key](/docs/apis/overview#profile-key-format) of a User Profile. Format: `Profile-Key: PROFILE_KEY` ## Path Parameters **id** `string` *required* Ayrshare Post ID from /post ```bash cURL theme={"system"} curl \ -H "Authorization: Bearer API_KEY" \ -X GET https://api.ayrshare.com/api/post/TBEAAqAMMJoweA9wKHUl ``` ```javascript JavaScript theme={"system"} const API_KEY = "API_KEY"; fetch("https://api.ayrshare.com/api/post/TBEAAqAMMJoweA9wKHUl", { method: "GET", headers: { "Authorization": `Bearer ${API_KEY}` } }) .then((res) => res.json()) .then((json) => console.log(json)) .catch(console.error); ``` ```python Python theme={"system"} import requests headers = {'Authorization': 'Bearer API_KEY'} r = requests.get('https://api.ayrshare.com/api/post/TBEAAqAMMJoweA9wKHUl', headers=headers) print(r.json()) ``` ```php PHP theme={"system"} [!NOTE] > > WhatsApp is messaging-only on Ayrshare, so you cannot publish posts to it. Use the > [Messaging API](/docs/apis/messages/overview) for WhatsApp conversations. Start publishing posts with the [/post POST endpoint](/docs/apis/post/post). ## Approval Workflow If your publishing workflow requires approval before sending a post, set the `requiresApproval` field to `true`. This is equivalent to pausing the post until the `approved` parameter is set to `true`. ### Approval Workflow Example The post will have a status of "awaiting approval" until the `approved` parameter is set to `true` via the [/post PATCH endpoint](/docs/apis/post/update-post). **1. Publish with parameters** Publish your post using the [/post endpoint](/docs/apis/post/post) with the `requiresApproval` field as `true`. You may also include standard parameters such as `scheduleDate`. **2. Status is awaiting approval** The post will be in a status of "awaiting approval" and will be held until approval is granted. **3. Approve the post** Update the post with the [/post PATCH operation](/docs/apis/post/update-post) setting the `approved` field as `true`. The post will now be sent at the scheduled time. **4. Use notes** Optional: Set `notes` on the post for reference, such as who needs to approve the post. ```json Publish the Post with Approval theme={"system"} { "requiresApproval": true, "notes": "need approval by John Smith" // optional } ``` > [!WARNING] > > If the `scheduleDate` is included and the date is in the past, the post will be published > immediately upon approval. ### Approval Workflow Video Please see the video below for an example of the approval workflow. [Social Approval Workflow API](https://www.youtube.com/embed/DtmTGgqQ-Mo) ## Auto Hashtags Add the most relevant hashtags to your post. `autoHashtag` is an object, or Boolean - see below, with the following parameters:
    • `max`: (optional) Integer of hashtags to add, range 1-10. Default 2.
    • `position`: (optional) String "auto" or "end". Auto adds the hashtags within the post or to the end. "end" adds hashtags just to the end.
    *A paid plan is required.* ```json theme={"system"} { "hashtags": { "max": 3, // optional: Integer range 1-10 "position": "auto" // optional: String "auto" or "end" } } ``` If you do not want to send any of the above options, pass the Boolean value `true` instead of an object. ```json theme={"system"} { "autoHashtag": true } ``` ## Auto Repost Automatically reposts your content multiple times at regular intervals, creating evergreen content that stays fresh and visible to your audience. A paid plan is required. Parameters: * `repeat`: (required) The number of times to repost the content. Must be between 1 and 10. * `days`: (required) The number of days between each repost. Must be at least 2 days. * `startDate`: (optional) When to start the repost schedule, in ISO-8601 UTC format. If not specified, the first post will be published immediately. You should use the `startDate` parameter in lieu of the top-level `scheduleDate` parameter. ```json Auto Repost theme={"system"} { "repeat": 2, // min 2, max 10 "days": 5, // min 2 "startDate": "2021-07-08T12:30:00Z" } ``` The response will include all the future scheduled reposts and an `autoRepostId` for each repost. ```json Auto Repost Response theme={"system"} { "status": "scheduled", "scheduleDate": "2025-06-13T12:30:00.000Z", "id": "eIT96IYEodNuzU4oMmwG", "refId": "9abf1426d6ce9122ef11c72bd", "autoRepostId": "F5wdoaOAAGtDQVciExSxL", "post": "The most important things are the hardest to say - Stephen King" } ``` When creating an auto repost an ID `autoRepostId` is assigned to track that series of posts. You may get all of the auto reposts for a post with the [History call](/docs/apis/history/get-history) with the `autoRepostId`. If you need to delete a repost, you can use the [DELETE call](/docs/apis/post/delete-post) with the post ID. > [!TIP] > > Important: When using auto-repost, ensure you follow each social network's posting frequency guidelines to avoid account restrictions. > > Note: The `autoRepost` feature cannot be used together with `scheduleDate`. If you include both parameters, `autoRepost` is used and `scheduleDate` is ignored. To choose when the reposts start, use the `startDate` parameter in `autoRepost`. ## First Comment Automatically add in a first comment, with media, after the post is published. For TikTok the comment is deferred until the video finishes processing (see [First Comment Processing Time](#first-comment-processing-time)). Posting the first comment on your own social media post can help kickstart engagement and set the tone for the discussion that follows. ```json theme={"system"} { "firstComment": { "comment": "My first comment", // required "mediaUrls": ["https://..."] // Facebook, LinkedIn, and X/Twitter only } } ``` ## First Comment Processing Time For most social networks, the API response is delayed because our system must (1) wait for the original post to be fully published, then (2) add the comment to that published post. This sequential process adds an approximately 20 second delay. **TikTok is handled differently.** TikTok processes videos asynchronously, so the post `id` is `"pending"` until TikTok's `post.publish.publicly_available` webhook resolves the real video id (see [TikTok Processing](/docs/apis/post/social-networks/tiktok#tiktok-processing)). The `/post` response therefore returns the TikTok first comment with `status: "pending"` right away, and the comment is posted automatically once TikTok finishes processing and the [`tikTokPublished` Scheduled Action webhook](/docs/apis/webhooks/actions#scheduled-action) fires. TikTok does not guarantee a processing time, so there is no fixed delay. **Important TikTok Note**: For first comments to work properly on TikTok, the post's `visibility` parameter must be set to `public`. A non-public video never receives the `publicly_available` webhook, so its first comment cannot be posted; in that case Ayrshare returns a comment error rather than leaving it pending. ## Idempotent Posts [Idempotency ](https://en.wikipedia.org/wiki/Idempotence) is an optional feature that ensures a request is only executed once, even if it is accidentally sent multiple times. When posting content using the API, you can include an optional `idempotencyKey` parameter in the request body to uniquely identify the operation. This allows you to safely retry the post request without the risk of creating duplicate posts. To use idempotency, add the `idempotencyKey` parameter to the JSON body of the `/post` POST request: ```json Idempotency Key theme={"system"} { "idempotencyKey": "Unique Key" } ``` The value of `idempotencyKey` should be a unique string per User Profile. If a request is made with the same `idempotencyKey` for a given User Profile, regardless of the post's state (success, error, pending, or deleted), an error will be returned, indicating that a duplicate key was found. > [!WARNING] > > The API must first accept and process the POST request to store the idempotency key and check for > duplicates. However, if multiple POST requests with the same `idempotencyKey` are sent > simultaneously or scheduled for the same posting time, the API may not detect the duplicate keys. > This is because the API processes these concurrent or simultaneously scheduled requests in > parallel, before it has a chance to register the idempotency key from any single request. As a > result, there's no guarantee that duplicate idempotent keys will be caught in these scenarios of > simultaneous submission or execution of scheduled posts. Using idempotency helps prevent the accidental creation of duplicate posts when retrying failed requests or handling network issues. However, it's still recommended to implement appropriate error handling and retry mechanisms in your application to handle potential failures gracefully. ## Image & Video Requirements Posting images and videos have different requirements for each network, but don't worry. Our system verifies your post before sending, so you'll get an error response if something is wrong. See the link below for details on image and video guidelines. > [card] [Image & Video Guidelines](/docs/media-guidelines/overview) ### Valid URL Be sure your media URL(s) are valid and directly access the media. A first test is trying the URL in a browser. If the image will not load or cannot be downloaded in a browser it will likely fail. For example, a DropBox URL that opens the DropBox web app will not work. > [!TIP] > > If you have a [Google Drive Share > URL](https://www.ayrshare.com/how-to-get-direct-download-urls-from-google-drive/) or a [Dropbox > Share URL](https://www.ayrshare.com/blog/how-to-get-direct-download-urls-from-dropbox/), you can just > use the URL in the `mediaUrls` parameter when publishing a post or comment. Ayrshare will > automatically convert the share URL to a download link. We verify the media URL by making a `HEAD` request. Please be sure the hosting provider is not blocking the `HEAD` request or the post will fail with a 403 error. For example, here is a `HEAD` request to the media URL: ```javascript Fetch HEAD Request theme={"system"} const run = async () => { const url = "https://img.ayrshare.com/012/gb.jpg"; return await fetch(url, { method: "HEAD" }) .then((res) => { if (res.ok) { return console.log("Success:", res.status, res.statusText); } else { return console.error("Error:", res.status, res.statusText, res); } }) .catch((error) => { return console.error("Error Catch:", error); }); }; run(); ``` #### Automated Media Protection Ayrshare includes built-in media protection that can detect and resolve certain media delivery issues during posting. When a post succeeds but a content issue was detected and resolved, each affected entry in `postIds[]` includes an optional `contentIssues` object so you can identify and fix the underlying issue: ```json theme={"system"} { "postIds": [ { "status": "success", "platform": "instagram", "id": "17878176260289172", "postUrl": "https://www.instagram.com/p/CP1dI9Hp_WO/", "contentIssues": { "originMediaHostFailed": true, "details": ["Media URL could not be retrieved by the social network. Successfully posted using Ayrshare automated media protection."] } } ] } ``` If you see `originMediaHostFailed` in your responses, review your media hosting configuration. See [Meta Media Crawler Blocked](/docs/help-center/technical-support/meta-media-crawler-blocked) for common causes and solutions. #### Spaces & Special Characters We recommend to avoid the following in your media URLs:
    • Spaces in the URL.
    • URL encoded spaces in the URL.
    • Special characters in the URL even if they are url encoded, such as accent marks é.
    For example: ```bash theme={"system"} https://img.ayrshare.com/012/test .webp ``` This URL fails because of the space `test .webp`. We also don't recommend using URL encoded spaces, such as `%20` in the URL - this can also cause issues with some social networks. And this Unsplash image: ```bash theme={"system"} https://unsplash.com/photos/a-house-in-the-middle-of-a-field-with-trees-in-the-background-znbmh2-cIj0 ``` This Unsplash image fails because it isn't directly accessing the image, but showing a web app. #### Sanitize File Names & URLs You can sanitize your file names with a regular expression such as `/[^a-z0-9\/\.]/gi`. ```javascript theme={"system"} const sanitizeFileName = (url) => url.replace(/[^a-z0-9\/\.]/gi, "_"); sanitizeFileName("tést .webp"); // t_st_.webp ``` or sanitize a URL ```javascript theme={"system"} const sanitizeUrl = (url) => { const [protocol, rest] = url.split("://"); const [domain, ...path] = rest.split("/"); const sanitizedPath = path.join("/").replace(/[^a-z0-9\/\.]/gi, "_"); return `${protocol}://${domain}/${sanitizedPath}`; }; // Output: https://img.ayrshare.com/012/t_st_.webp sanitizeUrl("https://img.ayrshare.com/012/tést .webp"); ``` ### Additional Information > [!NOTE] > >
      >
    • > If you're self hosting, make sure the URL can be externally accessed and doesn't require > special permissions. >
    • > >
    • > See below for how to handle videos with unknown extensions, often signed URLs such as AWS S3. >
    • > >
    • > If you are using a signed URL, such as S3, we recommend to set the URL expiration to at least > 7 days. This allows our team to assist with any questions you have on publishing the post. >
    • > >
    • > Test if the media URL exists with our [verify media tools](/docs/apis/media/verify-media-url). >
    • >
    ### Download Speed Be sure your media hosting has a fast connection, especially download speed. You can test your media hosting performance at [pingdom](https://tools.pingdom.com/). We recommend at least a *B rating*. ### Video Extension If your URL does not end in a known video extension such as `mp4`, you can use the `isVideo: true` field in the post to specify the `mediaUrl` is a video. Ayrshare will try to determine the file type, such as `MOV`. However, we recommend explicitly ending your video file with a known extension, such as `mp4`, since this has a higher success rate with the social networks. ### Image or Video Only A few social networks support sending media without post text. If you do not want post text included, send an empty string: `post: ""` The following social networks support no post/blank text: Facebook, Instagram, LinkedIn, Threads, TikTok, and X/Twitter. ### Testing Images and Videos Consider using the [generate random text and random image or video](/docs/quickstart#publish-test-posts) to speed up your testing. Stop trying to think up something different for each of your test posts! ## Line Breaks > [!NOTE] > > If you want to add line breaks (new lines) in a post, use the invisible line break `\u2063\n.`For example, `This is a new\u2063\nline.` > > We also recommend trying in Postman to see how the new line break is translated in your language of choice. For example, PHP often only uses a `\n` > > Some social networks do not currently support line breaks in the post text. ## Multi-Platform Posts and Media This feature allows you to customize your post content and media for different social networks within a single API call. You can specify unique text and/or images for each platform by using objects for the `post` and `mediaUrls` fields. 1. Use an object structure for `post` and/or `mediaUrls` fields. 2. Specify platform-specific content using platform names as keys. 3. Include a `default` key for content to be used on platforms not explicitly specified. ```json theme={"system"} { "post": { "instagram": "Great IG pic!", "facebook": "Great FB pic!", "default": "Great default pic!" }, "platforms": ["instagram", "facebook", "linkedin"], "mediaUrls": { "instagram": "https://img.ayrshare.com/012/gb.jpg", "linkedin": "https://img.ayrshare.com/012/gb.jpg", "default": "https://img.ayrshare.com/012/gb.jpg" } } ``` In the example above:
    • Instagram will use its specific text and image URL.
    • Facebook will use its specific text and the default image URL.
    • LinkedIn will use the default text and its specific image URL.
    > [!NOTE] > > If you need to post multiple images to different platforms, create separate posts for each > platform instead of using this multi-platform structure. ## Profile Keys Post to on behalf of user by providing users' Profile Keys as a body parameter and the additional data in the response. *Business or Enterprise Plan required.* > [card] [Profiles](/docs/apis/profiles/overview) ## Rich Text Posts You can add rich text such as "𝓗𝓮𝓵𝓵𝓸, how about a little 𝗯𝗼𝗹𝗱 𝘁𝗲𝘅𝘁 and 𝘪𝘵𝘢𝘭𝘪𝘤𝘴 𝘵𝘦𝘹𝘵 and an x₂?". You can use rich text on networks such as Twitter, Facebook, LinkedIn, Telegram, and Instagram. If posting to Reddit, please use [Reddit-flavored Markdown formatting](https://www.reddit.com/wiki/markdown#wiki_new_reddit-flavored_markdown). HTML elements are used to specify the type of rich text, which is translated into unicode. For example: ```json theme={"system"} { "post": "Hello, how about a little bold text and italics text and an x2?" "platforms": ["twitter"] } ``` ### HTML Elements | HTML | Example | | - | - | | \Nice One!\ | **Nice One!** | | \Hello, world!\ | **Hello, world!** | | \World\ | *World* | | normal \italics \bold italics\\ | normal *italics **bold italics*** | | \`\Hello\, world!\` | `Hello, world!` | | \`\Hello\, world!\` | **`Hello`**`, world!` | | \123\ | 𝟷𝟸𝟹 | | \Hello\ | 𝓗𝓮𝓵𝓵𝓸 | | x\2\ | x₂ | | x\2\ | x² | ### CSS Codes | Code | Example | Result | | - | - | - | | \u00B0 | It's 25\u00B0C today! | It's 25°C today! | | \u2063\n | This is a new\u2063\nline. | This is a new line. | ## Schedule Posts ### Create Scheduled Posts You can schedule future posts by specifying the `scheduleDate` parameter with the datetime in Zulu/UTC. Zulu Time, also known as Coordinated Universal Time (UTC), is the world standard for time. For example, use format `YYYY-MM-DDThh:mm:ssZ` and send as `2026-07-08T12:30:00Z`. Please see [utctime](https://www.utctime.net/) for more examples. ```json {5} theme={"system"} { "post": "Hello, world!", "mediaUrls": ["https://img.ayrshare.com/012/gb.jpg"], "platforms": ["facebook", "instagram"], "scheduleDate": "2023-07-08T12:30:00Z" } ``` Please see [https://www.utctime.net/](https://www.utctime.net/) on how to convert your local time to Zulu/UTC time. If the scheduled datetime is in the past, the post will immediately be sent. > [!NOTE] > > If a `mediaUrl` is included with a scheduled post, the media must be available at the scheduled > publication time. For example, if the post is scheduled to be published on March 5, 2026, the > media must be available on March 5, 2026. > [!TIP] > > Error Handling for Scheduled vs Immediate Posts > > There is an important difference in how validation errors are handled between immediate and scheduled posts: > > * Immediate Posts > * When publishing immediately (without scheduleDate), if one platform fails validation checks, the other platforms will continue to be processed. For example, if a post exceeds Twitter's character limit but is valid for Facebook and Instagram, the post will fail on Twitter but still be published to Facebook and Instagram. > * Scheduled Posts > * When scheduling posts for future publication (with scheduleDate), all platforms must pass initial validation checks before the post is scheduled. If any platform fails these pre-validation checks, the entire scheduling operation will be rejected and an error will be returned immediately. > * However, some platform-specific errors may not be detected until the actual posting time. In these cases, the scheduled post will attempt to publish to all platforms, and individual platform failures will be reported in the final results without affecting the other platforms. ### Check a Scheduled Post Status You can check the status of a scheduled post several ways:
    • Set up the [Webhook Scheduled Action](/docs/apis/webhooks/actions#scheduled-action) to automatically receive the status of the scheduled post. This is available for the Business plan and is the recommended method.
    • Get the status of a scheduled post with the [GET call](/docs/apis/post/get-post) with the post ID.
    • Check the status in the Ayrshare Dashboard. First switch to the User Profile the post was published under, then go to "Posts" page and search using the Ayrshare Post ID.
    ### Pause Scheduled Posts You may pause scheduled posts that have not yet been published. Pausing a scheduled post will prevent it from being published until the post has been unpaused. Use the [PATCH call](/docs/apis/post/update-post) to pause or unpause the scheduled post. Please note if a post is unpaused and the `scheduleDate` is in the past the post will immediately be published. Consider updating the `scheduleDate` before unpausing. ## Shorten Links Links in a post can be shortened using the Ayrshare [link shortener](/docs/apis/links/overview). You can turn on the automatic link shortening with the `shortenLinks` parameter when sending a post. [Max Pack required](/docs/additional/maxpack). ```json {4} theme={"system"} { "post": "Hello, world with a link https://www.ayrshare.com", "platforms": ["linkedin"], "shortenLinks": true } ``` ## Unsplash Images The following fields are available for the `unsplash` body parameter:
    • Random Image: `random` returns a random Unsplash image.
    • Search Based Image: value String search term ; e.g. `money` will select a random image based on money.
    • Image IDs: value Array of Ids; e.g. \["HubtZZb2fCM"] of image [https://unsplash.com/photos/HubtZZb2fCM](https://unsplash.com/photos/HubtZZb2fCM)
    ```json {4,5,6} theme={"system"} { "post": "Hello, world!", "platforms": ["instagram"], "unsplash": "random", "unsplash": "search term", // unsplash: "money" "unsplash": ["unsplash image ID"] // unsplash: ["HubtZZb2fCM"] } ``` > [!NOTE] > > If copying an Unsplash URL to post in `mediaUrls`, please be sure to copy the image address and > not just the URL. Please see this > [example](/docs/help-center/technical-support/get-an-unsplash-image-url) for more information. # Publish a Post API: Post to Any Social Network Source: https://www.ayrshare.com/docs/apis/post/post POST /post Publish to Facebook, Instagram, X, LinkedIn, TikTok, YouTube, and more with one Ayrshare API call. See parameters, code examples, and supported media. > [!NOTE] > > **Targeting X/Twitter?** Starting March 31, 2026, all X operations require your own API credentials. After linking X via OAuth, include these 2 headers in your request: > > `X-Twitter-OAuth1-Api-Key` — Your API Key (Consumer Key) > > `X-Twitter-OAuth1-Api-Secret` — Your API Key Secret (Consumer Secret) > > **One-time setup per Ayrshare account.** You create one X Developer App and reuse the same API Key and Secret across every sub-profile / end-user you link. You do *not* create a new app per customer. > > Not linked yet? See the [full setup guide](/docs/dashboard/connect-social-accounts/x-twitter-byo-keys) to connect your X account. > > Your keys are never logged or stored by Ayrshare. Publish posts to the social networks you or your users have linked. If you want to publish posts to User Profiles, please see the [/profiles endpoint](/docs/apis/profiles/overview) for more details. Please be sure to add your `API_KEY`, and the `PROFILE_KEY` if publishing to a User Profile, in the [Authorization header](/docs/apis/overview#authorization). The API Key can be found in the Ayrshare Developer Dashboard under the API Key page. See the [Post API Overview](/docs/apis/post/overview) for more details on posting options. ## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` **Profile-Key** `string` [Profile Key](/docs/apis/overview#profile-key-format) of a User Profile. Format: `Profile-Key: PROFILE_KEY` ## Body Parameters **post** `string` *required* The post text sent to the social networks specified in the platforms parameter. See here for [advanced options](/docs/apis/post/overview), including how to include URLs and rich text. You may send an empty string `""` to publish with no text. **platforms** `array` *required* Social media platforms to post. Accepts an array of Strings with values: `bluesky`, `facebook`, `gmb`, `instagram`, `linkedin`, `pinterest`, `reddit`, `snapchat`, `telegram`, `threads`, `tiktok`, `twitter`, or `youtube`. Please note: use `facebook` for Facebook Pages, and `gmb` for Google Business Profile. Use `all` to post to all linked social networks. Also include the required fields for all social networks. E.g. `title` must be included in `youTubeOptions` if `youtube` is linked. **mediaUrls** `array` An array of image or video URLs to include in the post. Please see [/media endpoint](/docs/apis/media/overview) to learn more. URLs must be secure and begin with `https://`. If the URL has special characters, e.g. ñ, please encode the special characters before sending. Videos require a paid plan. Please see here for [Image and Video Requirements](/docs/apis/post/overview#image-&-video-requirements) and [other advanced options](/docs/apis/post/overview). **isVideo** `boolean` (default: false) Ayrshare will try to determine the media type based on the file extension in the URL (.mp4). You can explicitly set the media as a video if the URL does not end in a known video extension, such as animated GIFs. Please see [video extension](/docs/apis/post/overview#video-extension) for details. **scheduleDate** `string` The datetime to schedule a future post. Accepts a UTC date time. For example, use format `YYYY-MM-DDThh:mm:ssZ` and send as `2026-07-08T12:30:00Z`. Please see [utctime](https://www.utctime.net/) for more examples. Also see [schedule posts](/docs/apis/post/overview#schedule-posts) for details. **validateScheduled** `boolean` (default: true) By default, scheduled posts are checked (pre-validated) for issues such as media requirements before they are accepted. If any problems are found by Ayrshare checks, you will receive an immediate error response and the post will not be scheduled. When the scheduled date is reached, the post will be published, at which time the final success or error response will be sent either via webhook or the [/history endpoint](/docs/apis/history/overview). To skip this pre-validation step, set `validateScheduled` to `false`. We recommend keeping validation enabled for scheduled posts to catch errors early. Otherwise, the post will be scheduled and you will only receive an error when it is published. Please see [scheduled webhook actions](/docs/apis/webhooks/actions#scheduled-action) for more details. **firstComment** `object` Add in a first comment automatically after posting. See [first comment](/docs/apis/post/overview#first-comment) for details. **disableComments** `boolean` (default: false) Disable comments for the post. Only available for Instagram, LinkedIn, and TikTok. **shortenLinks** `boolean` (default: false) Shorten links in the post for all platforms using [Ayrshare's link shortener](/docs/apis/links/overview). Only URLs starting with https will be shortened. [Max Pack required](/docs/additional/maxpack) for link shortening. Please see here for using [3rd party link shorteners](/docs/apis/links/overview#custom-link-domain). **autoSchedule** `object` Please see [auto-schedule](/docs/apis/auto-schedule/overview) for details. **autoRepost** `object` Automatically reposts your content multiple times at regular intervals, creating evergreen content that stays fresh and visible to your audience. See [auto repost](/docs/apis/post/overview#auto-repost) for details. **autoHashtag** `object` See [auto hashtags](/docs/apis/post/overview#auto-hashtags) for details. **unsplash** `string` See [unsplash](/docs/apis/post/overview#unsplash-images) for details. **blueSkyOptions** `object` See [Bluesky details](/docs/apis/post/social-networks/bluesky). **faceBookOptions** `object` See [Facebook details](/docs/apis/post/social-networks/facebook). **gmbOptions** `object` See [Google Business Profile details](/docs/apis/post/social-networks/google). **instagramOptions** `object` See [Instagram details](/docs/apis/post/social-networks/instagram). **linkedInOptions** `object` See [LinkedIn details](/docs/apis/post/social-networks/linkedin). **pinterestOptions** `object` See [Pinterest details](/docs/apis/post/social-networks/pinterest). **redditOptions** `object` See [Reddit details](/docs/apis/post/social-networks/reddit). **snapChatOptions** `object` See [Snapchat details](/docs/apis/post/social-networks/snapchat). **telegramOptions** `object` See [Telegram details](/docs/apis/post/social-networks/telegram). **threadsOptions** `object` See [Threads details](/docs/apis/post/social-networks/threads). **tikTokOptions** `object` See [TikTok details](/docs/apis/post/social-networks/tiktok). **twitterOptions** `object` See [X/Twitter details](/docs/apis/post/social-networks/x-twitter). **youTubeOptions** `object` See [YouTube details](/docs/apis/post/social-networks/youtube). **requiresApproval** `boolean` (default: false) See [approval workflow](/docs/apis/post/overview#approval-workflow) for details. **randomPost** `boolean` (default: false) [Generate random post text](/docs/quickstart#random-quote) for testing. `randomPost: true` will ignore the `post` field. **randomMediaUrl** `boolean` (default: false) [Generate a random media image](/docs/quickstart#random-image) for testing. `randomMediaUrl: true` will ignore the `mediaUrls` field. **idempotencyKey** `string` An optional unique ID associated with the post. Duplicate IDs will be rejected. Please see [idempotency](/docs/apis/post/overview#idempotent-posts) for details. **notes** `string` Set notes on a post that can be retrieved via the [/history endpoint](/docs/apis/history/overview). Notes are for reference only and do not affect the post. ```bash cURL theme={"system"} curl \ -H "Authorization: Bearer API_KEY" \ -H 'Content-Type: application/json' \ -d '{ "post": "Today is a great day!", "platforms": ["twitter", "facebook", "instagram", "linkedin"], "mediaUrls": ["https://img.ayrshare.com/012/gb.jpg"] }' \ -X POST https://api.ayrshare.com/api/post ``` ```javascript JavaScript theme={"system"} const API_KEY = "API_KEY"; fetch("https://api.ayrshare.com/api/post", { method: "POST", headers: { "Content-Type": "application/json", "Authorization": `Bearer ${API_KEY}` }, body: JSON.stringify({ post: "Today is a great day!", // required platforms: ["bluesky", "facebook", "instagram", "linkedin", "twitter"], // required mediaUrls: ["https://img.ayrshare.com/012/gb.jpg"] //optional }), }) .then((res) => res.json()) .then((json) => console.log(json)) .catch(console.error); ``` ```python Python theme={"system"} import requests payload = {'post': 'Today is a great day!', 'platforms': ['bluesky', 'facebook', 'instagram', 'linkedin', 'twitter'], 'mediaUrls': ['https://img.ayrshare.com/012/gb.jpg']} headers = {'Content-Type': 'application/json', 'Authorization': 'Bearer API_KEY'} r = requests.post('https://api.ayrshare.com/api/post', json=payload, headers=headers) print(r.json()) ``` ```php PHP theme={"system"} "Today is a great day!", "platforms" => ["bluesky", "facebook", "instagram", "linkedin", "pinterest", "twitter"], "mediaUrls" => ["https://img.ayrshare.com/012/gb.jpg"] ]; curl_setopt_array($curl, [ CURLOPT_URL => 'https://api.ayrshare.com/api/post', CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_POSTFIELDS => json_encode($data), CURLOPT_HTTPHEADER => [ 'Authorization: Bearer API_KEY', // Replace 'API_KEY' with your actual API key 'Content-Type: application/json' ], ]); $response = curl_exec($curl); curl_close($curl); echo $response; ``` ```go Go theme={"system"} package main import ( "bytes" "encoding/json" "log" "net/http" ) func main() { message := map[string]interface{}{ "post": "Today is a great day!", "platforms": []string{"bluesky", "facebook", "instagram", "linkedin", "twitter"}, "mediaUrls": []string{"https://img.ayrshare.com/012/gb.jpg"} } bytesRepresentation, err := json.Marshal(message) if err != nil { log.Fatalln(err) } req, _ := http.NewRequest("POST", "https://api.ayrshare.com/api/post", bytes.NewBuffer(bytesRepresentation)) req.Header.Add("Content-Type", "application/json; charset=UTF-8") req.Header.Add("Authorization", "Bearer API_KEY") res, err := http.DefaultClient.Do(req) if err != nil { log.Fatal("Error:", err) } res.Body.Close() } ``` ```csharp C# theme={"system"} using System; using System.Net.Http; using System.Text; using System.Threading.Tasks; namespace PostPOSTRequest_csharp { class Post { private static readonly HttpClient client = new HttpClient(); static async Task Main(string[] args) { string API_KEY = "API_KEY"; string url = "https://api.ayrshare.com/api/post"; // Set up request headers client.DefaultRequestHeaders.Add("Authorization", "Bearer " + API_KEY); // Prepare JSON content string json = "{\"post\" : \"Today is a great day!\"," + "\"platforms\" : [ \"bluesky\", \"facebook\", \"instagram\", \"linkedin\", \"twitter\" ]," + "\"mediaUrls\" : [ \"https://img.ayrshare.com/012/gb.jpg\" ]}"; var content = new StringContent(json, Encoding.UTF8, "application/json"); try { // Send POST request HttpResponseMessage response = await client.PostAsync(url, content); response.EnsureSuccessStatusCode(); // Read response string responseBody = await response.Content.ReadAsStringAsync(); Console.WriteLine(responseBody); } catch (HttpRequestException e) { Console.WriteLine($"Error: {e.Message}"); } } } } ``` ```ruby Ruby theme={"system"} require 'httparty' # gem install httparty res = HTTParty.post("https://api.ayrshare.com/api/post", headers: {Authorization: "Bearer API_KEY"}, body: { post: "Today is a great day!", platforms: ['bluesky', 'facebook', 'instagram', 'linkedin', 'twitter'], mediaUrls: ["https://img.ayrshare.com/012/gb.jpg"] }).body puts res ``` ```json 200: Success theme={"system"} { "status": "success", "errors": [], "postIds": [ { // Details at https://www.ayrshare.com/docs/apis/post/social-networks/bluesky "status": "success", "id": "at://did:plc:n7atrjd22xgkmgwig6dzlhzd/app.bsky.feed.post/3lez7fwx452", // Bluesky Social Post ID "cid": "bafyreie6n475cd3ynr6sfacvohu5qgjibcooxnug6zcbghkwnrwi5stafy", // Bluesky Content ID "postUrl": "https://bsky.app/profile/madworlds.bsky.social/post/3lez7fwx4572", "platform": "bluesky" }, { // Details at https://www.ayrshare.com/docs/apis/post/social-networks/facebook "status": "success", "id": "104923907983682_108329000309742", // Facebook Social Post ID "platform": "facebook", "postUrl": "https://www.facebook.com/104923907983682_108329000309742", }, { // Details at https://www.ayrshare.com/docs/apis/post/social-networks/google "status": "success", "id": "3837985438581442258", // Google Business Profile Social Post ID "postUrl": "https://local.google.com/place?id=5229466225881728772&use=posts&lpsid=CM", "type": "localPosts", "platform": "gmb" }, { // Details at https://www.ayrshare.com/docs/apis/post/social-networks/instagram "status": "success", "platform": "instagram", // Instagram Social Post ID "id": "17878176260289172", "postUrl": "https://www.instagram.com/p/CP1dI9Hp_WO/", "usedQuota": 12, "contentIssues": { // Optional — only present when Ayrshare detected and resolved a content issue "originMediaHostFailed": true, "details": ["Media URL could not be retrieved by the social network. Successfully posted using Ayrshare automated media protection."] } }, { // Details at https://www.ayrshare.com/docs/apis/post/social-networks/linkedin "status": "success", "id": "urn:li:share:7282181682126807041", // LinkedIn Social Post ID "postUrl": "https://www.linkedin.com/feed/update/urn:li:share:7282181682126807041", "owner": "urn:li:organization:77682157", "platform": "linkedin" }, { // Details at https://www.ayrshare.com/docs/apis/post/social-networks/pinterest "status": "success", "id": "42995371460659062", // Pinterest Social Post ID "postUrl": "https://www.pinterest.com/pin/429953714606062/", "platform": "pinterest" }, { // Details at https://www.ayrshare.com/docs/apis/post/social-networks/reddit "status": "success", "id": "1hvdvof", // Reddit Social Post ID "postUrl": "https://www.reddit.com/r/test/comments/1hvdvof/reddit_post_title/", "platform": "reddit" }, { // Details at https://www.ayrshare.com/docs/apis/post/social-networks/snapchat "status": "success", "id": "921ed204-e123-5b08-a9ce-zx489f1f38c5", // Snapchat Social Post ID "mediaId": "V6noC6UOQgOcABCDEgFZEwAAgd3F0cnp1eWtxZAb9PsH-MXb9PsIWAAAAAA", // Snapchat Media ID "postUrl": "https://www.snapchat.com/add/samsmith1920/921ed204-e123-5b08-a9ce-zx489f1f38c5", "type": "stories", "ended": "2025-05-23T13:04:30.545Z", "platform": "snapchat" }, { // Details at https://www.ayrshare.com/docs/apis/post/social-networks/telegram "status": "success", "id": 635, // Telegram Social Post ID "postUrl": "https://t.me/c/1424847122/635", "platform": "telegram" }, { // Details at https://www.ayrshare.com/docs/apis/post/social-networks/tiktok "status": "success", "idShare": "v_pub_url~v2.7456954878846683182", "id": "pending", // TikTok Social Post ID - see https://www.ayrshare.com/docs/apis/post/social-networks/tiktok "isVideo": true, "platform": "tiktok" }, { // Details at https://www.ayrshare.com/docs/apis/post/social-networks/twitter "status": "success", "id": "1288899996423983105", // X/Twitter Social Post ID "platform": "twitter", "postUrl": "https://x.com/handle/status/1288899996423983105" }, { // Details at https://www.ayrshare.com/docs/apis/post/social-networks/youtube "status": "success", "id": "3oQeP-kTsbo", // YouTube Social Post ID "postUrl": "https://youtu.be/3oQeP-kTo", "platform": "youtube" } ], "id": "RhrbDtYh7hdSMc67zC8H" // Ayrshare Post ID used for delete, analytics, comments, etc. } ``` ```json 200: Success for Scheduled theme={"system"} { "status": "scheduled", "scheduleDate": "2023-04-01T10:04:12Z", "id": "IUiaqFkQP96UJJXYjRpv", // Ayrshare Post ID used for delete, comment, analytics, etc. "refId": "9abf1426d6ce9122effdeeddfdfdfd", "post": "Genius is eternal patience. - Michelangelo" } ``` ```json 200: Success with Profile Key theme={"system"} { "status": "success", "posts": [ { "status": "success", "errors": [], "postIds": [ { "status": "success", "id": "1869166036466991888", "postUrl": "https://twitter.com/wondrouswaffles/status/1869", "platform": "twitter" }, { "status": "success", "id": "106638148652344_601623445855888", "postUrl": "https://www.facebook.com/106638148652329/posts/6016", "platform": "facebook" } ], "id": "bVQotNtxgXAUmLtqmw2", "refId": "b68bdcabb379be2cf1186c1e595449804b232sa", "profileTitle": "The Best Profile", "post": "Formal education will make you a living. Self education will make you a fortune. - Jim Rohn" } ] } ``` ```json 200: Success Scheduled Post and Profile Key theme={"system"} { "status": "success", "posts": [ { "status": "scheduled", "scheduleDate": "2023-04-01T10:04:12Z", "id": "qvu8gysraodz2WFZgRX7", // Ayrshare Post ID used for delete, comment, analytics, etc. "refId": "9abf1426d6ce9122effdeeddfdfdfd", "profileTitle": "Best Profile", "post": "I never thought of myself as being handsome or good-looking or whatever. I always felt like an outsider. - Elton John" } ] } ``` ```json 400: Bad Request theme={"system"} { "status": "error", "errors": [ { "action": "post", "status": "error", "code": 156, "message": "Youtube does not seem to be linked with Ayrshare. Please confirm the linkage on the Social Accounts page in your dashboard. .../ayrshare.com/additional-info/troubleshooting", "platform": "youtube" }, { "action": "post", "status": "error", "code": 110, "message": "Status is a duplicate.", "post": "Today is a great day", "platform": "twitter" } ], "postIds": [], "id": "0OGBzZssN5hxy8dMSRaD" // Ayrshare Post ID used for delete, comment, analytics, etc. } ``` ```json 400: Bad Request with Profile Key theme={"system"} { "status": "error", "posts": [ { "status": "error", "errors": [ { "action": "post", "status": "error", "code": 156, "message": "Instagram is not linked. Please confirm the linkage on the Social Accounts page in the dashboard. https://www.ayrshare.com/docs/help-center/overview", "platform": "instagram" } ], "postIds": [], "id": "ekftQJ0hFB1Fx6bnM33", "refId": "9abf1426d6ce9122effdeeddfdfdfd", "profileTitle": "Best Profile", "post": "The most common way people give up their power is by thinking they don't have any. - Alice Walker" } ] } ``` ```javascript 500: Internal Server Error theme={"system"} { "status": "error", "errors": [ { "action": "post", "status": "error", "code": 107, "message": "Facebook Error: This status update is identical to the last one you posted. Try posting something different, or delete your previous update.", "platform": "facebook" } ], "postIds": [], "id": "6APU4qqI7XO7JM3BOy6B" // Ayrshare Post ID used for delete, comment, analytics, etc. } ``` # Retry a Post Source: https://www.ayrshare.com/docs/apis/post/retry-post PUT /post/retry Retry publishing a post that failed with an error status. The retried post is treated as a scheduled post, and you can retry each failed post only once. > [!NOTE] > > **Targeting X/Twitter?** Starting March 31, 2026, all X operations require your own API credentials. After linking X via OAuth, include these 2 headers in your request: > > `X-Twitter-OAuth1-Api-Key` — Your API Key (Consumer Key) > > `X-Twitter-OAuth1-Api-Secret` — Your API Key Secret (Consumer Secret) > > **One-time setup per Ayrshare account.** You create one X Developer App and reuse the same API Key and Secret across every sub-profile / end-user you link. You do *not* create a new app per customer. > > Not linked yet? See the [full setup guide](/docs/dashboard/connect-social-accounts/x-twitter-byo-keys) to connect your X account. > > Your keys are never logged or stored by Ayrshare. Retry to publish a post that failed with a `status` of `"error".` A retried post will be treated as a scheduled post, thus the final status can be obtained via the [/history](/docs/apis/history/overview) endpoint or the [scheduled action webhook](/docs/apis/webhooks/actions#scheduled-action). While the post is being retried, the `status` will be `"pending"`. You may check on the status of the retry by calling the [GET post endpoint](/docs/apis/post/get-post). The post can be retried *only once*. Recommend to only use if the error message indicates a retry is possible. > [!NOTE] > > When retrying a failed social media post, first verify that the post hasn't already been > published. Social networks may sometimes report errors even when they've successfully processed > and published the post. ## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` **Profile-Key** `string` [Profile Key](/docs/apis/overview#profile-key-format) of a User Profile. Format: `Profile-Key: PROFILE_KEY` ## Body Parameters **id** `string` *required* The top-level Ayrshare Post ID. The original post must have a status of "error". ```bash cURL theme={"system"} curl \ -H "Authorization: Bearer API_KEY" \ -H 'Content-Type: application/json' \ -d '{"id": "s8k2jsk0pl"}' \ -X PUT https://api.ayrshare.com/api/post/retry ``` ```javascript JavaScript theme={"system"} const API_KEY = "API_KEY"; fetch("https://api.ayrshare.com/api/post/retry", { method: "PUT", headers: { "Content-Type": "application/json", Authorization: `Bearer ${API_KEY}` }, body: JSON.stringify({ id: "s8k2jsk0pl" // required }) }) .then((res) => res.json()) .then((json) => console.log(json)) .catch(console.error); ``` ```python Python theme={"system"} import requests payload = {'id': 's8k2jsk0pl'} headers = {'Content-Type': 'application/json', 'Authorization': 'Bearer API_KEY'} r = requests.put('https://api.ayrshare.com/api/post/retry', json=payload, headers=headers) print(r.json()) ``` ```php PHP theme={"system"} 's8k2jsk0pl' // Replace with your actual ID ]); $curl = curl_init($apiUrl); curl_setopt_array($curl, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_CUSTOMREQUEST => 'PUT', CURLOPT_HTTPHEADER => $headers, CURLOPT_POSTFIELDS => $data ]); $response = curl_exec($curl); if ($response === false) { echo 'Curl error: ' . curl_error($curl); } else { echo json_encode(json_decode($response), JSON_PRETTY_PRINT); } curl_close($curl); ``` ```csharp C# theme={"system"} using System; using System.Net.Http; using System.Text; using System.Threading.Tasks; namespace PostRetryPOSTRequest_csharp { class PostRetry { private static readonly HttpClient client = new HttpClient(); static async Task Main(string[] args) { string API_KEY = "API_KEY"; string url = "https://api.ayrshare.com/api/post/retry"; // Set up request headers client.DefaultRequestHeaders.Add("Authorization", "Bearer " + API_KEY); // Prepare JSON content string json = "{\"id\": \"s8k2jsk0pl\"}"; var content = new StringContent(json, Encoding.UTF8, "application/json"); try { // Send PATCH request HttpResponseMessage response = await client.PutAsync(url, content); response.EnsureSuccessStatusCode(); // Read response string responseBody = await response.Content.ReadAsStringAsync(); Console.WriteLine(responseBody); } catch (HttpRequestException e) { Console.WriteLine($"Error: {e.Message}"); } } } } ``` ```go Go theme={"system"} package main import ( "bytes" "encoding/json" "log" "net/http" ) func main() { message := map[string]interface{}{ "id": "s8k2jsk0pl" } bytesRepresentation, err := json.Marshal(message) if err != nil { log.Fatalln(err) } req, _ := http.NewRequest("PUT", "https://api.ayrshare.com/api/post/retry", bytes.NewBuffer(bytesRepresentation)) req.Header.Add("Content-Type", "application/json; charset=UTF-8") req.Header.Add("Authorization", "Bearer API_KEY") res, err := http.DefaultClient.Do(req) if err != nil { log.Fatal("Error:", err) } res.Body.Close() } ``` ```json 200: Success theme={"system"} { "status": "pending", // The post will be retried "id": "N7kaDiZAfwc544OBKlgc" // Ayrshare Post ID } ``` ```json 400: Already Retried theme={"system"} { "action": "post", "status": "error", "code": 329, "message": "This post was already retried and cannot be retried again." } ``` # Bluesky API Source: https://www.ayrshare.com/docs/apis/post/social-networks/bluesky Post to Bluesky with links, hashtags, images, and videos, plus supported and unsupported features, mentions, alternative text, and character limits. ## Posting to Bluesky JSON for a basic post with a link, hashtag, and image/video using the Bluesky API. Please see [Bluesky Media Guidelines](/docs/media-guidelines/bluesky) and [Bluesky Authorization](/docs/dashboard/connect-social-accounts/bluesky) for more information.: ```json Bluesky Post with Image theme={"system"} { "post": "The best Bluesky image post ever #best", // Max 300 characters or empty string "mediaUrls": ["https://img.ayrshare.com/012/gb.jpg"], "platforms": ["bluesky"] } ``` ```json Bluesky Post with Video theme={"system"} { "post": "The best Bluesky video post ever https://www.google.com", // Max 300 characters or empty string "mediaUrls": ["https://img.ayrshare.com/012/vid.mp4"], "platforms": ["bluesky"] } ``` ### Bluesky Supported Features
    • Bluesky supports text, images (up to 4), video (only 1), and posts with links or emojis.
    • Up to 4 images or one videos can be sent in a single post.
    • The post text is limited to 300 characters.
    • Animated GIFs are supported and sent as videos.
    • Bluesky does support hashtags and mentions (@handle).
    • Alt text on images or videos.
    • See [Bluesky Media Guidelines](/docs/media-guidelines/bluesky) for more information.
    • Link previews in posts are supported. Include a link in the post text and the Ayrshare API will automatically add a link preview to the post.
    ### Bluesky Unsupported Features
    • Video thumbnails are not yet supported by Bluesky.
    ## Bluesky Mentions Mention another Bluesky handle by adding `@handle` in the post text. For example: ```json Bluesky Post with Mention theme={"system"} { "post": "The best Bluesky image post ever @handle", "platforms": ["bluesky"] } ``` > [!WARNING] > > Please review the [important rules](/docs/testing/post-verification#mentions) on mentions. ## Alternative Text Add Bluesky alternative text, also known as alt text, to an image or video. Bluesky alt text is an accessibility feature used for additional user info and screen readers. Use the `altText` in the `blueSkyOptions` object. ```json Bluesky Alt Text theme={"system"} { "blueSkyOptions": { // Array of Alt Texts "altText": ["This is my best pic", "😃 here is the next one"] } } ``` Each alt text must correspond to an image or video in the `mediaUrls` array. The alt text will be applied to each media item in order. ## Character Limits Please see [Bluesky Character Limits](/docs/help-center/technical-support/character-limits#bluesky-character-limits) for more information. # Facebook Page API Source: https://www.ayrshare.com/docs/apis/post/social-networks/facebook Post to Facebook Pages with carousel images, Reels, Stories, location tagging, audience targeting, Page mentions, Meta Business Suite drafts, and GIFs. > [!NOTE] > > If you have issues connecting Facebook, please see the [troubleshooting > guide](/docs/help-center/technical-support/facebook-posting-issues). > [!NOTE] > > If your media is hosted on a server or CDN you control, make sure Meta's publishing > crawler can fetch it. When Meta can't fetch your remotely-hosted media — including Facebook > Reels media ingested through the shared media-upload path — you may get the dedicated, > non-retryable Ayrshare error code `479`. See > [Meta Media Crawler Blocked](/docs/help-center/technical-support/meta-media-crawler-blocked) to resolve it. > [!NOTE] > > Posting an AI-generated image? When Ayrshare converts a HEIC, HEIF, or AVIF image to JPEG it > writes a small XMP packet carrying only the `Iptc4xmpExt:DigitalSourceType` AI > disclosure, when the image declares one, and Facebook renders that disclosure as an "AI content" > label. No other metadata is carried over: not EXIF, and nothing else from the original XMP, so a > location, a creator name or a copyright line can't be published by accident. The C2PA cryptographic > signature does not survive a re-encode. See [Image Metadata and Content > Credentials](/docs/media-guidelines/overview#image-metadata-and-content-credentials). ## Posting to Facebook Posting using the Facebook API requires connecting a Facebook Page. Facebook does not allow personal accounts to be connected. JSON for a basic post with a link and image to a Facebook page: ```json Facebook Post with Image theme={"system"} { "post": "The best FB post ever #best https://www.facebook.com", // empty string is allowed "mediaUrls": ["https://img.ayrshare.com/012/gb.jpg"], "platforms": ["facebook"] } ```
    • Facebook will automatically show a preview of the link in the post unless there is an image or video included. In the above example the image will show. Removing the image will cause the link preview to show.
    • If your video doesn't end in a known video extension such as mp4, please use the `isVideo` parameter. See the [/post endpoint](/docs/apis/post/post) for details.
    • Facebook also supports sending media without post text. If you do not want post text included send an empty String `post: ""`.
    • See [Facebook Media Guidelines](/docs/media-guidelines/facebook-pages) and [Facebook Authorization](/docs/dashboard/connect-social-accounts/facebook) for more information.
    > [!WARNING] > > Facebook's API does not support mixing images and videos in a single post. You > can include multiple images OR a single video, but not both. ## Carousel Images Post Facebook Carousel images through Ayrshare's API with the `carousel` body parameter. ```json Facebook Carousel Post theme={"system"} { "faceBookOptions": { "carousel": { // The URL of the "See More At" button - required "link": "URL of See More At...", "items": [ // 2 min, 10 max elements in the array { "name": "Image name", // optional "link": "URL when image clicked", // required "picture": "URL of image" // required }, { "name": "Image name", "link": "URL when image clicked", "picture": "URL of image" } ] } } } ```
    • The top-level `link` parameter is the URL at the end of the carousel.
    • The `items` is an array of object containing the following values. Minimum 2 elements and max 10 elements in the items array.
    • `picture` - URL of the carousel image.
    • `link` - URL when the image is clicked.
    • `name` - (optional) Image displayed in each image card.
    **Note:** Do not use the `media_urls` with `carousel`. If `media_urls` is used, `carousels` will be ignored. Please see our [Facebook Carousel Guide](https://www.ayrshare.com/blog/post-a-series-of-facebook-images-as-a-carousel/) for more information. ## Facebook Reels You can post a video to Facebook Reels with the following `facebookOptions`. > [!NOTE] > > Meta enforces a limit of 30 Reels per connected Facebook Page within any 24-hour period to prevent > spam and ensure platform quality. This is a rolling limit, meaning it counts the most recent 24 > hours, not calendar days. ```json Facebook Reels Post theme={"system"} { "post": "The description of the video", "platforms": ["facebook"], "mediaUrls": ["https://img.ayrshare.com/012/reel.mp4"], "faceBookOptions": { "reels": true, "title": "Super title for the Reel", // optional "thumbNail": "https://img.ayrshare.com/012/reel-thumbnail.jpg", // optional "collaborators": ["123456789012345"] // optional Facebook Page ID, one supported in v1 } } ```
    • `reels`: Set to `true` to post the video to Reels. Required if you want to post to Facebook Reels.
    • `title`: The title of the video with a maximum of 255 characters. Over 255 characters will be truncated. Optional.
    • `thumbNail`: The thumbnail (cover image) of the video. The thumbnail should a URL to a JPEG or PNG file. The thumbnail should be less than 10MB in size. Optional.
    • `collaborators`: Invite one Facebook Page ID as a collaborator on the Reel. Facebook collaborators are only supported on Facebook Reels, not on standard Facebook feed posts, photos, or non-Reel video posts.
    • Ayrshare currently forwards one collaborator Page ID per Facebook Reel in v1. If you send more than one value, only the first non-empty value is forwarded and the rest are dropped with a warning in the `warnings` array. Ayrshare does not check that the value is a valid Page ID first, so make sure the first value in the list is the Page you want to invite.
    • Meta limits collaborator invites to 10 invitations per Facebook Page within a rolling 24-hour period.
    • Facebook Reel collaborators must be other Facebook Pages. Personal Facebook profiles are not supported for collaborator invites on this API.
    • The Reel can publish successfully while Facebook still declines the collaborator invite. When that happens, the Reel post returns `success` with a warning in the `warnings` array rather than failing. Facebook declines the invite when the collaborator Page is not eligible — most commonly because it is in the same Meta Business portfolio as the posting Page (a Page cannot collaborate with itself), does not allow collaborators, or is not eligible for Facebook Reels collaboration.
    • Collaborators are not supported with Facebook's own drafts or scheduling (`faceBookOptions.draft` or `faceBookOptions.scheduledPublishDate`), because Ayrshare sends the invite at the moment the Reel publishes. To schedule a Reel with a collaborator, use Ayrshare scheduling (the top-level [`scheduleDate`](/docs/apis/post/post#body-parameters) parameter) instead — Ayrshare publishes the Reel at the scheduled time and then sends the invite.
    Please see the [Reels API video requirements](/docs/media-guidelines/facebook-pages#reels) or an [example of using the Facebook Reels API](https://www.ayrshare.com/blog/facebook-reels-api-how-to-post-fb-reels-using-a-social-media-api/). After the Reel publishes, Ayrshare sends the collaborator invite through Meta's Facebook Reels API. You can later check the invitation status with the shared [Get Collaborator Request Status API](/docs/apis/utils/instagram-get-collaborator). > [!WARNING] > > Meta occasionally has issues processing Facebook Reels for some accounts. Even though an error response may be returned, the Reel might still have been published. > > If you encounter errors for Facebook Reels, we recommend checking the Reel status. If the post *has not been* published you may try [posting once more](/docs/apis/post/retry-post). This is often successful. > > You may use the URL contained in the `verifyReelsUrl` field to check the status. Be sure to use the same Profile Key as your original post. Wait until the time in `verifyReelsIn` to check the Reel status (10 minutes). If a HTTP error code of `400` is returned, the post failed and you may try the post one more time. > > ```json Facebook Reels Post Error theme={"system"} > { > "action": "post", > "status": "error", > "code": 108, > "message": "Facebook Error: Facebook cannot process your post at this time. Please try again. ", > "detailsData": { > "id": "104619420979033_36930641143", > "verifyReelsUrl": "https://api.ayrshare.com/history/36930641143?searchPlatformId=true&platform=facebook", > "verifyReelsIn": "2024-01-26T00:07:05.170Z", > "postUrl": "https://www.facebook.com/1046194209_36930641143", > "reels": true > }, > "retryAvailable": true, > "platform": "facebook" > } > ``` ## Facebook Stories Publish Facebook Stories to Facebook Pages either as a photo or as a video.
    • Facebook Stories do not support post text - any text provided in the `post` field, including mentions, will be ignored.
    • A photo or video uploaded for a story cannot have been used in a previously published post.
    • A video story cannot exceed 60 seconds.
    • Turn on [Stories Archive](https://www.facebook.com/help/2058997717520567) to be able to GET stories.
    ```json Facebook Stories Post theme={"system"} { "post": "", // Ignored by stories "platforms": ["facebook"], "mediaUrls": ["https://img.ayrshare.com/012/stories.mp4"], "faceBookOptions": { "stories": true } } ``` Please see the [Stories API image and video requirements](/docs/media-guidelines/facebook-pages#stories). ## Media Captions Set a Facebook caption for each media image posted. Accepts an array of string caption text. Each array must correspond to a media url. E.g. \["This is my best pic", "😃 here is the next one"] refers to the 1st and 2nd urls in `mediaUrls`. ```json Facebook Media Captions theme={"system"} { "faceBookOptions": { "mediaCaptions": ["This is my best pic", "😃 here is the next one"] } } ``` ## Location Tagging A Facebook location tag is specified by a `locationId,` which is a Facebook Page ID or Facebook Page name. For example, Facebook page Id of the [Guggenheim Museum](https://www.facebook.com/guggenheimmuseum) is `7640348500` or Facebook page name `"@guggenheimmuseum"`. Pages must be associated with a physical location. ```json Facebook Location Tagging theme={"system"} // Using the Facebook Page Id - must be associated with a location { "faceBookOptions": { "locationId": 7640348500 // Guggenheim Museum Page Id } } ``` You can look up the `locationId` (Page Id) with the [brand endpoint](/docs/apis/listen/search/fb-page-search). Please note that the Page must have a location listed or the locationId will return an error. Not supported on text posts, images, reels, and stories. Videos are not supported by Meta for location tagging. ## Audience Targeting When creating a Page post on Facebook, you have the option to limit its visibility to a specific audience using two types of targeting: 1. Facebook Targeting: This allows you to define the audience for your post based on factors such as age, gender, location, and relationship. By setting these parameters, you can ensure that your post is shown only to people who meet the specified criteria. 2. Facebook Feed Targeting: This option enables you to further refine the visibility of your post within the News Feeds of your targeted audience. You can use either targeting option individually or combine them. ### Targeting Facebook targeting [limits the audience](https://www.facebook.com/help/352402648173466) for publishing content to specific demographics. Anyone not in these demographics will not be able to view this content. This will not override any Page-level demographic restrictions that may be in place. Available demographic fields `targeting`:
    • `ageMin`: Limit the minimum age that can view the posts. Accepted values of 13, 15, 18, 21, or 25\. Other ages will be rejected.
    • `countries`: Also known as geofencing, you can limit the countries that can view the post. Array of [country codes](/docs/iso-codes/country). The maximum number of countries that can be targeted is 25\. If you need to target more than 25 countries, or restrict certain countries, please see [Facebook Page Country Restrictions](/docs/help-center/technical-support/facebook-page-country-restrictions).
    ```json Facebook Targeting theme={"system"} { "faceBookOptions": { "targeting": { "ageMin": 18, "countries": ["DE", "BR"] } } } ``` ### Feed Targeting Anyone in these Facebook feed targeting groups are more likely to see this post, others are less likely, but may still see it anyway. Use targeting, see above, if you want to guarantee the restriction. Use the `feedTargeting` object in the `faceBookOptions` object. Available demographic fields for `feedTargeting`:
    • `ageMin`: Limit the minimum age that can view the posts.. Integer value 13 or higher. Default is 0\.
    • `ageMax`: Limit the maximum age that can view the posts. Integer value 65 or lower.
    • `countries`: Required. Also known as geo targeting, you can indicate the countries that should be able to view the post. Array of [country codes](/docs/iso-codes/country).
    • `collegeYears`: Target the college graduation age to view the post. Array of integers for graduation year from college.
    • `educationStatuses`: Target the education status to view the post. Array of integers for targeting based on education level. Use `1` for high school, `2` for undergraduate, and `3` for alum.
    • `genders`: Target the gender to view the post. Array of integers for targeting specific genders. `1` targets all male viewers and `2` females. Default is to target both.
    • `relationshipStatuses`: Target the relationship status to view the post. Array of integers for targeting based on relationship status. Use `1` for single, `2` for 'in a relationship', `3` for married, and `4` for engaged. Default is all types.
    ```json Facebook Feed Targeting theme={"system"} { "faceBookOptions": { "feedTargeting": { "ageMax": 30, "ageMin": 26, "countries": ["DE", "BR"], "genders": [1, 2], "relationshipStatuses": [1, 2] } } } ``` ## Alternative Text Add Facebook alternative text, also known as alt text, to an image or video. Facebook alt text is an accessibility feature used for additional user info and screen readers. Use the `altText` in the `faceBookOptions` object. ```json Facebook Alt Text theme={"system"} { "faceBookOptions": { // Array of Alt Texts "altText": ["This is my best pic", "😃 here is the next one"] } } ``` Each alt text must correspond to an image or video in the `mediaUrls` array. The alt text will be applied to each image in order. ## Video Thumbnail Set a Facebook video thumbnail (cover image). Send a remote URL of a PNG or JPG file that is the same dimensions as the video and less than 10 MB. URL should end in .png or .jpg. ```json Example Facebook Video Thumbnail theme={"system"} { "faceBookOptions": { "thumbNail": "https://octodex.github.com/images/Fintechtocat.png" } } ``` ## Video Title Add a Facebook video title with the `title` parameter. ```json Facebook Video Title theme={"system"} { "faceBookOptions": { "title": "The best video ever!" } } ``` ## Animated GIFs Only one `mediaUrls` URL is allowed in the array when posting a Facebook animated GIF. If the media URL does not end in ".gif" or ".GIF", set the `isVideo` field to `true`. ```json Facebook Animated GIF theme={"system"} { "randomPost": true, "platforms": ["facebook"], // Set to true if the GIF URL does not end in .gif or .GIF "isVideo": false, // Only one URL is allowed in the array "mediaUrls": ["https://img.ayrshare.com/012/cat.gif"] } ``` ## Facebook Page Mentions You can mention another Facebook Page, also known as Facebook mentions or Facebook tagging, by including the Facebook Page name or Page ID in the post text. Facebook does not allow mentions or tags of personal profiles or groups. Include a mention with the following @mention: `@page-name` or `@[page-id]` ### Mention with Page Name An example post text with a Page name. You can find the Page name from the Facebook URL, e.g. [https://www.facebook.com/Ayrshare](https://www.facebook.com/Ayrshare) ```json Facebook Page Mention with Page Name theme={"system"} { "post": "This is the best social media api by @Ayrshare" } ``` Ayrshare will make a best attempt to find the matching Page based on the name, but sometimes a match cannot be found. A more reliable method is using a Page ID. ### Mention with Page ID An example post text with a Page ID; note the use of \[]. Please see below on how to find the Page ID. ```json Facebook Page Mention with Page ID theme={"system"} { "post": "This is the best social media api by @[738681876342836]" // Ayrshare Page ID } ``` The Facebook Page ID will resolve to the matching page and ***notify the Page mentioned***. > [!WARNING] > > Use Page mentioning with caution by only mentioning Pages you are associated. You should be careful to never spam. > > When you mention a Page, **the owner of the Page will be notified via an alert and email**. If several Pages complain about you mentioning them, Facebook could ban your account. Generally you should only tag Pages that you have an established relationship with. > > The Facebook Page being mentioned **must allow** other Pages to mention/tag their Page. Mentions can be enabled in the Page's general settings under "Others Tag this Page". > > Please review the [important rules](/docs/testing/post-verification#mentions) on mentions. ### Find a Facebook Page ID #### Brand Endpoint You can look up either a Facebook user or Facebook Page using the [Brand endpoints](/docs/apis/brand/overview). #### At Facebook.com 1. From the Facebook News Feed, click **Pages** in the left side menu. 2. Click your Page name to go to your Page. 3. Click **About** at the top of your Page. If you don't see it, click **More**▼. 4. Scroll down to find your **Page ID** below **MORE INFO**. or if you want to find the ID of a page your don't own:
    • If the Facebook Page has a URL such as`https://www.facebook.com/ayrshare-1234567890` then the ID is ***1234567890***.
    • If the Facebook Page has a URL such as:
    `https://www.facebook.com/pages/ayrshare/123466789203` then the Page ID is the number at the end, such as **123466789203**. You can test that the Page ID is correct by going to `https://facebook.com/page-id` and it will resolve to the Page. You can also try a 3rd party Facebook Page look up tool, which is not supported by Ayrshare, such as: [https://lookup-id.com/](https://lookup-id.com/) If all other methods fail, you can go to the Facebook page in a browser and view the page source. In Chrome, right click to select "View Source". Then search for either `owning_profile_id` or `profile_id` to find the Page ID. > [!WARNING] > > Please review the [important rules](/docs/testing/post-verification#mentions) on mentions. ### Enable Page Mentions The Page must allow mentions and tagging in posts and comments. By default this is enabled, but you can verify by logging into facebook.com and switching to your Page. 1. Viewing as your Page, click your page's **profile image** in the top right corner. 2. Click **Settings & Privacy** -> **Settings**. 3. In the Settings menu, click **Privacy**. 4. Click **Page and Tagging**. 5. Scroll down to **Reviewing**. 6. Toggle settings to allow tag and mentions. ## Meta Business Suite ### Draft Posts Create a Facebook draft post that appears in the [Meta Business Suite Draft](https://business.facebook.com/latest/posts/draft_posts) tab. Facebook draft posts are posts that you have started writing, but have not yet published. You can save draft posts to come back to later, or you can schedule them to be published at a later date. Add the `draft` parameter to `faceBookOptions` to send the post as a draft. Available for text, images, video, and reels posts. ```json Facebook Draft Post theme={"system"} { "faceBookOptions": { "draft": true } } ``` ### Schedule Publish Time Set a schedule publish time for Facebook posts that appear in the [Meta Business Suite Scheduled Posts](https://business.facebook.com/latest/posts/scheduled_posts) tab. Facebook scheduled posts are posts that you have created and scheduled to be published at a later date and are managed in the Meta Business Suite. This can be useful for planning your social media posts in advance, or for publishing posts at times when you know your audience is most active. Available for text, images, video, and reels posts. > [!WARNING] > > The publish time must be greater than 10 minutes from the current time and within 29 days of the current date. > > Unless you need to manage Facebook posts in Meta Business Suite, we recommend using the standard [post schedule date](/docs/apis/post/overview#schedule-posts). Add the `scheduledPublishDate` parameter to `faceBookOptions` to schedule a post time. ```json Facebook Scheduled Post theme={"system"} { "faceBookOptions": { "scheduledPublishDate": "2023-09-28T21:44:06Z" // Future date in UTC } } ``` ## Link Preview A Facebook post will automatically create a link preview of a link in the post body. However, if you want to specify either a different link or force the link preview, use the `link` parameter with URL of the link. ```json Facebook Link Preview theme={"system"} { "faceBookOptions": { "link": "https://www.ayrshare.com/instagram-hashtag-guide/" } } ``` If a `mediaUrls` field is included in the post request, the media will be displayed instead of the link preview. ## Facebook Ads Facebook Ads are a way to promote your posts to a wider audience. > [card] [Facebook Ads](/docs/apis/ads/overview) ## Character Limits Please see [Facebook Character Limits](/docs/help-center/technical-support/character-limits#facebook-character-limits) for more information. ## Additional Information ### How do I add line breaks or rich text to a Facebook post? Facebook line breaks can be added to a post with a special [new line character](/docs/apis/post/overview#line-breaks). Rich text, such as bold or italic lettering, can be added to a Facebook post with a few [html elements](/docs/apis/post/overview#rich-text-posts). ### Why do I see "Published by Ayrshare" on a post? Don't worry, it only shows in the admin view. Please see the [troubleshooting guide](/docs/help-center/technical-support/facebook-shows-published-by-ayrshare) for more information. # Google Business Profile API Source: https://www.ayrshare.com/docs/apis/post/social-networks/google Publish Google Business Profile standard, image, video, event, and offer posts, with posting requirements, product categories, and call to action options. > [!NOTE] > > When Ayrshare converts a WebP, HEIC, HEIF, or AVIF image to JPEG for Google Business Profile it > preserves the image's color rendering and writes a small XMP packet carrying only the `Iptc4xmpExt:DigitalSourceType` AI disclosure, when the image declares one. No other metadata is carried over: not EXIF, and nothing else from the original XMP, so a location, a creator name or a copyright line can't be published by accident. The C2PA cryptographic signature > does not survive a re-encode. See [Image Metadata and Content > Credentials](/docs/media-guidelines/overview#image-metadata-and-content-credentials). ## Posting to Google Business Profile There are four types of Google Business Profile, formerly known as Google My Business, posts that can be created using the Google Business Profile API. In the below example, the `post` text is posted as an update to the user's Google business page. [Find out more](https://www.ayrshare.com/blog/google-my-business-what-is-gmb-why-you-need-it-and-how-to-use-it/) about Google Business Profile (GBP). > [!WARNING] > > You must [claim](https://support.google.com/business/answer/2911778) your Google Business Profile page before linking it with Ayrshare. Be sure to choose the Google account that is an admin of your GBP page during link authorization. The business profile must also be verified and public. > > You can access your GBP manage console and check the verification status at [https://business.google.com/](https://business.google.com/) Product posts cannot be created via the Google Business Profile API at this time. JSON for a basic post with just text to GBP, which will appear in the "What's New" section of : ```json Google Business Profile Post theme={"system"} { "post": "The best GMB ever #best https://www.google.com", "platforms": ["gmb"] // Please note to use `gmb` } ``` or JSON for a basic post with an image to GBP: ```json Google Business Profile Post with Image theme={"system"} { "post": "The best GMB ever #best https://www.google.com", "mediaUrls": ["https://img.ayrshare.com/012/gb.jpg"], "platforms": ["gmb"] } ```
    • If your video doesn't end in a known video extension such as mp4, please use the `isVideo` parameter. See the [/post endpoint](/docs/apis/post/overview) for details.
    • See [Google Business Profile Media Guidelines](/docs/media-guidelines/google-business-profile) and [Google Business Profile Authorization](/docs/dashboard/connect-social-accounts/google-business) for more information. more information.
    ## Google Business Profile Posting Requirements Please be sure your post does not contain the following. Google will reject your post and the post will show as "This post is no longer available" or with a "Rejected" label in the Google Console:
    • Phone numbers. Google will reject all posts containing phone numbers.
    • Spam, false claims, or false representation.
    • Off-topic post that doesn't pertain to your business.
    • Duplicate content.
    • Inappropriate content.
    • Offensive language.
    Please see [Google Business Profile's Content Policy](https://support.google.com/business/answer/7213077?hl=en) for more information. ## Standard Post (What's New) A standard local post appears in the "*Posts*" GBP section and is categorized as "What's New". It can contain post text, an image, or a Call to Action ([see below](/docs/apis/post/social-networks/google#call-to-action)). Videos are not supported for to be published to the What's New section via the Google Business Profile API. Please see [Image or Video Post](#image-or-video-post) for details on how to publish videos to Google Business Profile. *These posts will appear in the "**Posts -> What's New**" section in the GMB management console.* ```json Google Business Profile Standard Post theme={"system"} { "post": "A wonderful post", "platforms": ["gmb"], "mediaUrls": ["https://img.ayrshare.com/012/gb.jpg"], // optional - images only "gmbOptions": { "callToAction": { // optional "actionType": "learn_more", "url": "https://www.ayrshare.com" } } } ``` ## Image or Video Post You can publish Image or Video posts to Google Business Profile. Images and videos will appear in the "**Photos**" section in the GBP management console. ### Requirements
    • Only one image or one video can be added to your GBP location, which will appear in the "Photos" section of the GBP console.
    • Only one image or video is allowed per post.
    • The post text is required by the endpoint, but is not used by GBP.
    • Call to Action is not supported for image or video posts.
    • Be sure to include the `isPhotoVideo` parameter in the `gmbOptions` object.
    ```json Google Business Profile Image Post {6} theme={"system"} { "post": "What an image!", "platforms": ["gmb"], "mediaUrls": ["https://img.ayrshare.com/012/gb.jpg"], // required "gmbOptions": { "isPhotoVideo": true, //required "category": "product" // optional } } ``` ### Video Processing Google at times takes extra time to process uploaded videos. You can view the status of the media in the Google Business Profile console: 1. Go to your [Google Business Profile console](https://business.google.com/us/business-profile/). 2. Click the button "See your profile". You will br brought to the GBP page. 3. Click on the "Photos" tab. 4. You will see a list of photos and videos. 5. Click on the photo or video you want to view. > [!WARNING] > > If your video has a "pending" status, it is still being processed by Google. Please wait a few > minutes and refresh the page. Be sure your Google Business Profile has been verififed and the > profile is public. > > If the video seems stuck in "pending", this may be due to a bug at Google. > Please contact [Google Business support](https://support.google.com/business/gethelp) for > assistance. ### Product Category You can specify a product category for an image or video so the media is categorized in the "Photos" section of the GBP console. For example, if an image has the category "product", it will appear under the "Product" tab in Photos. Some categories are not allowed for certain business types. For example, Ayrshare could not use `food_and_drink` since we are not in the food/drink industry. Available Categories:
    • `cover`: Cover photo. A location has only one cover photo.
    • `profile`: Profile photo. A location has only one profile photo.
    • `logo`: Logo photo.
    • `exterior`: Exterior media.
    • `interior`: Interior media.
    • `product`: Product media.
    • `at_work`: At work media.
    • `food_and_drink`: Food and drink media.
    • `menu`: Menu media.
    • `common_area`: Common area media.
    • `rooms`: Rooms media.
    • `teams`: Teams media.
    ## Events Post Promote an event at your business. Events require a title, start and end dates, and can include a video. For example, a real estate agent may advertise an open house showing.
    • Events support Call to Action.
    • Include an optional image with the `mediaUrls` parameter.
    • Videos are not supported for Events.
    • Event posts will appear in the "**Posts -> Events**" section in the GBP management console.
    • The `post` text will be used for the Event Details.
    ```json Google Business Profile Event Post theme={"system"} { "post": "A great event!", // Event Details "platforms": ["gmb"], "mediaUrls": ["https://img.ayrshare.com/012/gb.jpg"], // optional "gmbOptions": { "event": { "title": "Check this event out.", // required "startDate": "2021-08-12T20:17:46.384Z", // required "endDate": "2021-09-12T20:17:46.384Z" // required } } } ``` ## Offers Post Provide promotional sales or offers from your business. Offers require a title as well as start and end dates and times. A "View offer" action button is automatically added to the post. You can also include a photo, description, coupon code, link, and terms and conditions with the post. For example, a toy store may advertise 20% off all beanie babies for a week. Include an image with the `mediaUrls` parameter. *Videos are not supported for Offers.* *These posts will appear in the "**Posts -> Offers**" section in the GBP management console.* ```json Google Business Profile Offers Post theme={"system"} { "post": "A great offer for everyone!", "platforms": ["gmb"], "gmbOptions": { "offer": { "title": "Great Sale.", // required "startDate": "2021-08-12T20:17:46.384Z", // required "endDate": "2021-09-12T20:17:46.384Z", // required "couponCode": "BOGO-JET-CODE", // required - max 58 characters "redeemOnlineUrl": "https://www.ayrshare.com", // required "termsConditions": "Offer only valid if you can prove you are a time traveler" // required } } } ```
    • `couponCode` has a maximum of 58 characters.
    ## Call to Action Provide general information about your business and appears as an action button. For example, a website might promote a *Learn More* button to redirect to their website. A Call to Action can be added to a Standard post or Event. Offers cannot have Call to Actions. Include an image with the `mediaUrls` parameter. ```json Google Business Profile Call to Action Post theme={"system"} { "post": "Take this action!", "platforms": ["gmb"], "mediaUrls": ["https://img.ayrshare.com/012/gb.jpg"], //optional "gmbOptions": { "callToAction": { "actionType": "order", // required "url": "https://www.ayrshare.com" // required for all action types but "call" } } } ``` The `actionType` is displayed as a button on the post. Possible values are:
    • **book**: Prompts a user to book an appointment, table, or something similar.
    • **order**: Prompts a user to order something.
    • **shop**: Prompts a user to browse a product catalog.
    • **learn\_more**: Prompts a user to see additional details on a website.
    • **sign\_up**: Prompts a user to register, sign up, or join something.
    • **call**: Prompts a user to call a business. The `url` field is not required for `call`. However, you must first have set a phone number in your Google Business Profile for the call action to appear on the post.
    The `url` parameter is the URL the user will be directed to upon clicking. Additional [Google Business Profile information](https://www.ayrshare.com/blog/google-my-business-what-is-gmb-why-you-need-it-and-how-to-use-it/). > [card] [Google Business Profile Linking](/docs/dashboard/connect-social-accounts/google-business) ## First Comment Google Business Profile does not support post comments, so the `firstComment` parameter is skipped for Google Business Profile-only posts. A `firstComment` warning/skip object is returned, but the post itself still succeeds, with no error raised. ## Google Business Profile Mentions While you can add a `@handle` to a Google Business Profile post, Google Business Profile does not support resolving mentions in the post text. The `@handle` will remain as plain text. ## Character Limits Please see [Google Business Profile Character Limits](/docs/help-center/technical-support/character-limits#google-business-profile-character-limits) for more information. # Instagram API Source: https://www.ayrshare.com/docs/apis/post/social-networks/instagram Publish Instagram carousels, Reels with music, trial reels, and Stories, plus thumbnails, alt text, user tags, collaborators, and AI content labels. > [!NOTE] > > If you have issues connecting Instagram, please see the [troubleshooting > guide](/docs/help-center/technical-support/facebook-or-instagram-linking-issues). > [!NOTE] > > If your media is hosted on a server or CDN you control, make sure Meta's publishing > crawler can fetch it. A hard media-fetch / crawler-block failure returns the dedicated, > non-retryable Ayrshare error code `479` ("social network could not download media from this URL"), > often with error code 138 or "Restricted by robots.txt" in the details as the underlying signal. > See [Meta Media Crawler Blocked](/docs/help-center/technical-support/meta-media-crawler-blocked) to resolve it. > [!NOTE] > > Posting an AI-generated image? When Ayrshare converts a WebP, HEIC, HEIF, or AVIF image to JPEG it > writes a small XMP packet carrying only the `Iptc4xmpExt:DigitalSourceType` AI > disclosure, when the image declares one, and Instagram renders that disclosure as an "AI info" > label. No other metadata is carried over: not EXIF, and nothing else from the original XMP, so a > location, a creator name or a copyright line can't be published by accident. The C2PA cryptographic > signature does not survive a re-encode. Note that `instagramOptions.autoResize` resizes > through a separate step that carries no metadata, so leave it off if the disclosure needs to reach > Instagram. See [Image Metadata and Content > Credentials](/docs/media-guidelines/overview#image-metadata-and-content-credentials). [Posting to Instagram API](https://www.youtube.com/embed/3F7mbVN0qrw) The Instagram API has the following requirements and restrictions.
    • Business or Creator Instagram Account connected with a Facebook Page - [see here](/docs/dashboard/connect-social-accounts/instagram).
    • Only 50 Instagram posts are allowed over a 24 hour period. See below for `usedQuota`
    • The `post` text may contain up to *5 hashtags* (e.g. #wildtimes) and *3 username mentions* (e.g. @natgeo).
    • @mention Instagram users will receive a notification.
    • Maximum 2,200 post characters.
    • Multi-image/videos posts are supported and sent as a carousel. You may send up to 10 videos and images.
    • Instagram does not support deletes via an API. Deletes must occur manually using the Instagram app.
    • If your Reel video doesn't end in a known video extension such as mp4, please use the `isVideo` parameter. See the [/post endpoint](/docs/apis/post/post) for details.
    • Instagram also supports sending media without post text. If you do not want post text included send an empty String `post: ""`.
    • See [Instagram Media Guidelines](/docs/media-guidelines/instagram) and [Instagram Authorization](/docs/dashboard/connect-social-accounts/instagram) for more information.
    ## Posting to Instagram JSON for a basic post with an image and hashtags to Instagram: ```json Instagram Post theme={"system"} { "post": "The best IG ever #best #awesome https://www.instagram.com", "mediaUrls": ["https://img.ayrshare.com/012/gb.jpg"], "platforms": ["instagram"] } ``` Hashtags are clickable in Instagram posts, but links are not. > [!NOTE] > > The aspect ratio of images & videos and the duration of video are very important to successfully post to Instagram. If they do not meet the requirements, the post will be rejected. > > [Please see the Instagram section of Image and Video Guidelines](/docs/media-guidelines/instagram). ## Instagram Business or Creator Account Your Instagram account must be a Business or Creator Account and connected with a Facebook Page. The set up is free and easy. See here for detailed instructions: > [card] [Instagram Linking](/docs/dashboard/connect-social-accounts/instagram) ## Carousel of Images and Videos You can post multiple images or Reel videos to Instagram as a carousel; up to a combined total of 10 images or videos may be used in a carousel. Just add your additional images or videos to the `mediaUrls` array, and the carousel will automatically be created. ```json Instagram Carousel Post theme={"system"} { // Max 10 images or videos "mediaUrls": ["https://url.com/image.jpg", "https://url.com/video.mp4"] } ``` > [!NOTE] > > Video URLs must end in a known extension such as `mp4`. The `isVideo` parameter is not supported > for Instagram carousels. ## Instagram Reels In Instagram a video post is called a Reel. You can post a video to Instagram Reels API with the following optional `instagramOptions` options. ```json Instagram Reels Options theme={"system"} { "instagramOptions": { "shareReelsFeed": true, "audioName": "The Weeknd - Blinding Lights", "thumbNail": "https://img.ayrshare.com/012/gb.jpg", // if used, thumbNailOffset will be ignored. "thumbNailOffset": 30000 } } ```
    • Please see the [Reels API video requirements](/docs/media-guidelines/instagram#reels) for details on the video requirements.
    • `shareReelsFeed`: Boolean set to `true` to indicate that the Reel *can* appear in both the **Feed** and **Reels** tabs or `false` to indicate the Reel *can* only appear in the **Reels** tab. This value is a hint to Instagram where you would like the Reel to appear, but neither value determines whether the Reel *actually* appears in the **Reels** or **Feed** tab because the Reel may not meet eligibility requirements or may not be selected by Instagram's algorithm.
    • `audioName`: String name of the audio music of your Reels media. You can only rename once, either while creating a reel or after from the audio page. For example, `"The Weeknd - Blinding Lights"`. This only sets the **display label** on audio you already supplied in the video. To attach a real licensed track from Instagram's catalog, use [`audioConfiguration`](/docs/apis/post/social-networks/instagram#adding-music-to-a-reel) instead.
    • `audioConfiguration`: Object that attaches a licensed track from Instagram's audio catalog. See [Adding music to a Reel](/docs/apis/post/social-networks/instagram#adding-music-to-a-reel). **Requires Instagram linked through Facebook Login.**
    • `thumbNail`: String URL of the Reel cover image (thumbnail). Please see here for [details on thumbNail](/docs/apis/post/social-networks/instagram#reels-thumbnails).
    • `thumbNailOffset`: Integer offset in milliseconds of the thumbnail frame. Please see here for [details on thumbNailOffset.](/docs/apis/post/social-networks/instagram#reels-thumbnails)
    Please see the [Reels API video requirements](/docs/media-guidelines/instagram#reels) or an [example of using the Instagram Reels API](https://www.ayrshare.com/blog/instagram-reels-api-how-to-post-videos-to-reels-using-a-social-media-api/). ## Adding Music to a Reel > [!WARNING] > > **Requires Instagram linked through Facebook Login.** Instagram's Audio API is only available to accounts connected via **Facebook Login**, which needs a Facebook Page connected to the Instagram account. Accounts connected with **Instagram Login** (the Ayrshare default) cannot search, look up, or attach audio, and every audio request returns `code: 514`. > > This is a Meta restriction, not an Ayrshare one. Meta's documentation states the Audio API "is only available on the Instagram API with Facebook Login. It is not supported on the Instagram API with Instagram Login." No permission or plan upgrade changes it. > > **Three things are required:** > > 1. An Instagram **Business** or **Creator** account > > 2. A **Facebook Page connected** to that Instagram account > > 3. Instagram linked to Ayrshare with **Instagram Login disabled**, so the Facebook Login flow is used > > See the [step-by-step setup guide](/docs/dashboard/connect-social-accounts/instagram#music-attach-eligibility-facebook-login-required). `instagramOptions.audioConfiguration` attaches a licensed track from Instagram's audio catalog to a Reel. Find a track with [audio search](/docs/apis/media/search-instagram-audio), then pass its `audioId` when you publish. ```json Reel with music theme={"system"} { "post": "New drop", "platforms": ["instagram"], "mediaUrls": ["https://img.ayrshare.com/012/vid.mp4"], "instagramOptions": { "audioConfiguration": { "audioId": "17901234567890123", "audioVolume": 80, "videoVolume": 20 } } } ```
    • `audioId`: **Required.** String track id from [audio search](/docs/apis/media/search-instagram-audio) or [audio lookup](/docs/apis/media/get-instagram-audio). Missing or empty returns `code: 516`.
    • `audioVolume`: Optional integer from `0` to `100` setting how loud the attached track plays.
    • `videoVolume`: Optional integer from `0` to `100` setting how loud the video's own audio plays underneath the track.
    > [!NOTE] > > Both volumes are **optional integers**. Omit them and Instagram applies its own default of `100`. > A decimal, a numeric string such as `"80"`, a value outside 0 to 100, or an explicit `null` is > rejected with `code: 101` before Instagram is called. `null` is not accepted as "unset" — omit the > field instead. ### Reels Only Music can be attached to **Reels only**. Stories, images, and carousels are rejected with `code: 515`. You do **not** need to set `reels: true` or `isVideo: true`. A video `mediaUrls` entry is detected as a Reel automatically, and an auto-detected Reel accepts `audioConfiguration` the same as an explicit one. Setting the flags explicitly still works and takes precedence. ### audioConfiguration and audioName Together Instagram allows only one track per Reel. If you send both, `audioConfiguration` wins, `audioName` is dropped, and a non-fatal warning with `code: 518` is returned in `postIds[].warnings[]`. The post still publishes. Send only `audioConfiguration` to attach a licensed track, or only `audioName` to set a display label on audio already in your video. ```json 200: Published with a warning theme={"system"} { "status": "success", "postIds": [ { "status": "success", "id": "17901234567890123", "postUrl": "https://www.instagram.com/reel/ABC123/", "usedQuota": 1, "warnings": [ { "action": "post", "status": "warning", "code": 518, "message": "The audioName value was ignored because audioConfiguration was also sent, and Instagram can attach only one track per Reel. Send only audioConfiguration to attach a licensed track, or only audioName to set the display label.", "feature": "audioName" } ], "platform": "instagram" } ] } ``` `warnings[]` is nested on the **`postIds` entry for that platform**, not lifted to the top level, so read it from `postIds[0].warnings` rather than `warnings`. ### Audio Errors | Code | Meaning | | - | - | | 101 | `audioVolume` or `videoVolume` is not an integer from 0 to 100, or is `null`. | | 514 | Instagram is linked with Instagram Login. The Audio API requires Facebook Login. | | 515 | `audioConfiguration` was sent on something that is not a Reel. | | 516 | `audioId` is missing or empty. | | 517 | Instagram rejected the track as invalid, region-restricted, or not licensed for this use. | | 518 | Warning, not an error. `audioName` was ignored because `audioConfiguration` was also sent. | You may also set a [Reels cover url](/docs/apis/post/social-networks/instagram#reels-thumbnails) and [location & user tags](/docs/apis/post/social-networks/instagram#user-tags-and-locations). ## Trial Reels A trial reel is a Reel that is published only to non-followers when it is first posted, allowing you to test how a Reel performs with a fresh audience before it reaches your existing followers. Set `trialParams.graduationStrategy` in `instagramOptions` to publish a Reel as a trial. ```json Instagram Trial Reel theme={"system"} { "post": "Testing this with a fresh audience first", "mediaUrls": ["https://img.ayrshare.com/random/portrait1.mp4"], "platforms": ["instagram"], "instagramOptions": { "trialParams": { "graduationStrategy": "MANUAL" } } } ``` `graduationStrategy` controls how a trial reel later "graduates" — i.e. becomes visible to your followers as well. It is required when `trialParams` is provided and must be one of:
    • `"MANUAL"` — the post stays a trial reel until you manually graduate it from inside the Instagram app.
    • `"SS_PERFORMANCE"` — Meta auto-graduates the Reel based on early performance against non-followers.
    > [!NOTE] > > Graduation itself (promoting a published trial reel to followers) is not currently exposed by > Meta's API and must be performed manually in the Instagram app. Ayrshare will add a graduation > endpoint once Meta exposes one. ### Trial reel restrictions The trial reel request is rejected at the Ayrshare edge — before any Meta call — when any of these conditions are not met:
    • Exactly one media URL ending in `.mp4` or `.mov` (case-insensitive). Carousels are not supported.
    • `instagramOptions.stories` must not be `true`. Stories cannot be trial reels.
    • `graduationStrategy` must be present and exactly `"MANUAL"` or `"SS_PERFORMANCE"` (case-sensitive).
    Failures return one of three Ayrshare error codes — see [Instagram Trial Reel Errors](/docs/errors/errors-ayrshare#instagram-specific-error-codes) (`447`, `448`, `449`) for the full payloads. ## Instagram Stories You can post a single image or video as an Instagram Story with the following `instagramOptions`. Instagram stories disappear after 24 hours. ```json Stories Post theme={"system"} { "post": "The description of the video", "mediaUrls": ["https://img.ayrshare.com/random/portrait1.mp4"], "instagramOptions": { "stories": true } } ``` Please see the [Stories API requirements](/docs/media-guidelines/instagram#stories).
    • Instagram Stories do not support post text - any text provided in the `post` field, including mentions, will be ignored.
    • Stories expire after 24 hours.
    • Stories can be published from both Instagram Business and Creator accounts.
    • Instagram Stories do not support collaborators.
    • Publishing stickers (i.e., link, poll, location) is not supported by Instagram.
    ## Reels Thumbnails You may select a frame of the Reel as the thumbnail image or your own cover image (thumbnail) from an external URL. ```json Instagram Thumbnail theme={"system"} { "instagramOptions": { // milliseconds "thumbNailOffset": 30000, // If both thumbNail and thumbNailOffset includes, thumbNail will be used. "thumbNail": "https://img.ayrshare.com/012/gb.jpg" } } ``` The offset is the location in milliseconds of the thumbnail frame of the video Reel. Default value is `0`, which is the first frame of the Reel. If you specify both a thumbnail URL and a thumbnail offset, the thumbnail offset will be ignored. The Reel thumbnail must follow the [Reels thumbnail requirements](/docs/media-guidelines/instagram#reels-thumbnails). Signed URLs with redirects are not guaranteed to be compatible with cover URLs. We recommend a non-signed URL or use the [/media endpoint](/docs/apis/media/overview). ## Alternative Text Add Instagram alternative text, also known as alt text, to an image. Instagram alt text is an accessibility feature used for additional user info and screen readers.
    • Alt text supports up to 1,000 characters per image.
    • Instagram does not support alt text for Reels or Stories.
    Use the `altText` in the `instagramOptions` object. ```json Instagram Alt Text theme={"system"} { "instagramOptions": { // Array of Alt Texts "altText": ["This is my best pic", "😃 here is the next one"] } } ``` Each alt text must correspond to an image or video in the `mediaUrls` array. The alt text will be applied to each image in order. ## User Tags & Locations > [!NOTE] > > The Instagram user will be notified when you use their username in a post. Please be cautious to > not spam users or post with their username repeatedly. If you do, Instagram could suspend or > deactivate your account. An image or a Reel can be tagged with Instagram users and an image, video, or a Reel can be tagged with a location using the `instagramOptions` parameter. ### Location A location is specified by a `locationId,` which is a Facebook Page ID or Facebook Page name. For example, Facebook page Id of the [Guggenheim Museum](https://www.facebook.com/guggenheimmuseum) is `7640348500` or Facebook page name `"@guggenheimmuseum"`. Pages must be associated with a physical location. ```json Instagram Location theme={"system"} // Using the Facebook Page Id - must be associated with a location { "instagramOptions": { "locationId": 7640348500 // Guggenheim Museum Page Id } } // Using the Facebook Page name - must be associated with a location { "instagramOptions": { "locationId": "@guggenheimmuseum" // Guggenheim Museum Page name. Must begin with @ } } ``` You can look up the `locationId` (Page Id) with the [brand endpoint](/docs/apis/brand/overview). Please note that the Page must have a location listed or the locationId will return an error. > [!NOTE] > > Not supported on images or videos in carousels. ### User Tags Instagram tags allow you to tag other Instagram users in your post. Users are specified by a `userTags` containing an Array of objects with an Instagram username and x/y coordinates (image only). User tags can be added for single images or Reels, but not regular videos, multiple images, or Stories.
    • Usernames must be public Instagram accounts. Do not include the @ of the user handle.
    • `x` and `y` values must be `float` numbers that originate from the top-left of the image, with a range of `0.0`–`1.0`. Single images. Do not include with Reels or an error will occur.
    ```json Instagram User Tags theme={"system"} { "instagramOptions": { "userTags": [ { "username": "ayrshare", // Required: Instagram username "x": 0.5, // Required for Images, cannot be used with Reels "y": 1.0 // Required for Images, cannot be used with Reels }, { "username": "johnboy", // Required: Instagram username "x": 0.1, // Required for Images, cannot be used with Reels "y": 0.9 // Required for Images, cannot be used with Reels } ] } } ``` ## Instagram Mentions Mention another Instagram handle by adding @handle in the post text. For example you can mention the @ayrshare handle in the post text: ```json Instagram Mentions theme={"system"} { "post": "The best social media API @Ayrshare ever!", // empty string is allowed "mediaUrls": ["https://images.com/image.jpg"], "platforms": ["instagram"] } ``` > [!WARNING] > > The `@mentioned` user will be notified of the mention. Please review the [important > rules](/docs/testing/post-verification#mentions) on mentions. ## Collaboration Instagram collaboration allows you to co-author content with other Instagram accounts by [tagging others as collaborators](https://www.facebook.com/help/instagram/291200585956732). This allows you to designate other Instagram users as creators on your post. When tagged, these users receive an invitation to collaborate in the mobile app. If they accept, the post will also appear in their feed and be visible to their followers, extending the post’s reach and engagement potential. ### Collaborators The public original author can tag another public account as an Instagram collaborator. The other account will receive a message, which allows them to either accept or deny the request. If the other account accepts, the post will also show on their profile and be distributed to their followers in Instagram feed. The post’s header will attribute the content to both accounts. You may add collaborators to a Reel, image, or carousel. Tagging a private collaborator is not permitted via the Instagram API. While such a functionality exists within the Instagram app, platforms and their APIs are often not at [feature parity](/docs/help-center/product/are-social-networks-native-apps-and-the-apis-at-parity). > [!WARNING] > > Instagram Stories do not support collaborators. > [!WARNING] > >
      >
    • **Only invite collaborators you expect to `accept` your invite**.
    • > >
    • > If the [collaborator > responds](/docs/apis/post/social-networks/instagram#get-collaborator-request-status) with a > `declined` collaboration request, **do not re-invite them** until you contact them to > understand the reason for the decline. >
    • > >
    • > Repeated declines from the same or multiple users will put your Instagram and Ayrshare account > at risk of cancelation. >
    • > >
    • The original author can add or remove a collaborator at any time.
    • >
    Invite *up to three* collaborators with an array of public Instagram usernames. ```json Instagram Collaborators theme={"system"} { "instagramOptions": { "collaborators": ["ayrshare", "therock", "taylorswift"] // Up to three } } ``` These three collaborators will receive a message invitation in their Instagram mobile app and can accept or decline the invite, and then you may [check the invited user request status](/docs/apis/post/social-networks/instagram#get-collaborator-request-status). Please only invite collaborators who you know will accept your request or your Instagram account may be negatively affected. > [!WARNING] > > Note about invited collaborators: With a few exceptions, data on or about co-authored media can > only be accessed through the API by the user who published the media; collaborators are unable to > access this data via the API. ### Get Collaborator Request Status After inviting an Instagram collaborator, you can check the status of the request using the [Get Collaborator Request Status API](/docs/apis/utils/instagram-get-collaborator). ## AI Content Label Self-disclose AI-generated media so Instagram applies its **AI info** label to the published post. ```json Instagram AI Content Label theme={"system"} { "post": "Generated with a little help from a model", "platforms": ["instagram"], "mediaUrls": ["https://img.ayrshare.com/random/portrait1.mp4"], "instagramOptions": { "isAIGenerated": true } } ```
    • `isAIGenerated`: Boolean set to `true` to add Instagram's AI info label to the post. Default `false` — omitting the parameter publishes the post with no label.
    • Instagram shows the label as **AI info** beneath the account name. Opening it reads "\[account] added an AI label to this content".
    • Accepted values are `true`, `"true"`, `false`, and `"false"`. Any other value — `"yes"` or `1`, for example — never fails the request: the post publishes normally without the label, and the response adds a non-fatal `warnings` entry (`code: 497`, `feature: "isAIGenerated"`) listing the accepted values.
    • Applies to single images, single videos, Reels, Stories, and carousels.
    • For a [carousel](/docs/apis/post/social-networks/instagram#carousel-of-images-and-videos), the label applies to the carousel as a whole. Individual carousel items cannot be labeled separately.
    • This is a self-disclosure only. Ayrshare does not detect or infer whether your media is AI-generated. Instagram may separately label content its own systems detect as AI-generated.
    > [!NOTE] > > The Ayrshare [MCP Server](/docs/additional/mcp-action-server) takes `isAIGenerated` as a strict > boolean — pass `true`, not `"true"`. The REST API accepts both. > [!NOTE] > > The label cannot be added or removed after a post publishes. Set `isAIGenerated` at post time. ## Auto Image Resize > [!NOTE] > > Max Pack Required Images are automatically resized to 1080 x 1080 px to work with Instagram with the `autoResize` parameter. Please note, this will resize the image for all included platforms, so we recommend making one call for Instagram and a different /post call for additional platforms. ```json Instagram Auto Image Resize theme={"system"} { "post": "Let it go!", "platforms": ["instagram"], "mediaUrls": ["https://images.ayrshare.com/imgs/GhostBusters.jpg"], "instagramOptions": { "autoResize": true, // Max Pack "locationId": 7640348500, "userTags": [ { "username": "ayrshare", "x": 0.5, "y": 0.5 }, { "username": "ayrshare", "x": 0.3, "y": 0.2 } ] } } ``` ## Used Quota The Instagram response will include the current `usedQuota` for the number of Instagram posts done over a rolling 24-hour period. Only 50 Instagram posts are permitted by Instagram in a 24-rolling-hour period. ```json Instagram Used Quota theme={"system"} { "status": "success", "errors": [], "postIds": [ { "status": "success", "id": "17823977408036085", "postUrl": "https://www.instagram.com/p/CeBrkQuN1Kv/", "usedQuota": 15, "platform": "instagram" } ], "id": "l4FaPHSXWJNdmMfm3dIE", "refId": "65806e8d9efd78a58c05566a887043329dcdc76b", "post": "Luckily for Alice, the little magic bottle had now had its full effect." } ``` If the quota has been reached, an error message will be returned. ## Content Issues Ayrshare includes built-in media protection that can detect and resolve certain media delivery issues during posting. When a post succeeds but a content issue was detected and resolved, the response includes an optional `contentIssues` object. This allows you to identify and fix issues with your media hosting proactively. The `contentIssues` object is only present when an issue was detected and resolved — normal successful posts do not include it. | Field | Type | Description | | - | - | - | | `originMediaHostFailed` | boolean | The social network could not retrieve the media from the provided URL. Ayrshare's automated media protection resolved the issue and completed the post successfully. Consider reviewing your media hosting configuration — see [Meta Media Crawler Blocked](/docs/help-center/technical-support/meta-media-crawler-blocked) for common causes. | | `details` | array of strings | Human-readable descriptions of each detected issue. | ```json Content Issues Response Example theme={"system"} { "status": "success", "errors": [], "postIds": [ { "status": "success", "id": "17878176260289172", "postUrl": "https://www.instagram.com/p/CP1dI9Hp_WO/", "usedQuota": 12, "platform": "instagram", "contentIssues": { "originMediaHostFailed": true, "details": [ "Media URL could not be retrieved by the social network. Successfully posted using Ayrshare automated media protection." ] } } ], "id": "abc123", "refId": "65806e8d9efd78a58c05566a887043329dcdc76b", "post": "Your post text here" } ``` > [!TIP] > > If you see `originMediaHostFailed` in your responses, there may be an issue with your media hosting that prevents social networks from accessing your media. See [Meta Media Crawler Blocked](/docs/help-center/technical-support/meta-media-crawler-blocked) for detailed troubleshooting steps. ## Error Details > [!NOTE] > > When an Instagram media publish fails, the error object now surfaces Meta's underlying error text in the `details` field, alongside the Ayrshare `code` and `message`. This lets you distinguish between different root causes (for example, an aspect-ratio rejection versus a media-download failure) without contacting support. ```json Instagram Publish Error theme={"system"} { "status": "error", "errors": [ { "action": "post", "status": "error", "platform": "instagram", "code": 156, "message": "An error occurred while posting to Instagram.", "details": "The submitted image was not found. The aspect ratio is not supported. Please see https://developers.facebook.com/docs/instagram-api for more information." } ] } ``` The `message` is Ayrshare's stable, human-readable summary, while `details` echoes the raw text returned by Meta for the failed publish. ## Adding line breaks or rich text to an Instagram post Instagram line breaks can be added to a post with a special [new line character](/docs/apis/post/overview#line-breaks). Rich text, such as bold or italic lettering, can be added to an Instagram post with a few [html elements](/docs/apis/post/overview#rich-text-posts). ## Character Limits Please see [Instagram Character Limits](/docs/help-center/technical-support/character-limits#instagram-character-limits) for more information. ## Additional endpoints > [card] [Get Collaborator Request Status](/docs/apis/utils/instagram-get-collaborator) # LinkedIn API Source: https://www.ayrshare.com/docs/apis/post/social-networks/linkedin Post to LinkedIn with documents, multi image posts, mentions, media titles, alternative text, audience targeting, post visibility, and video thumbnails. > [!NOTE] > > Relying on embedded image metadata for AI disclosure? LinkedIn strips embedded metadata when it > ingests an image, so an `Iptc4xmpExt:DigitalSourceType` disclosure does not survive > LinkedIn's image processing and no AI label is applied. See [Image Metadata and Content > Credentials](/docs/media-guidelines/overview#image-metadata-and-content-credentials). The LinkedIn API has the following requirements and restrictions:
    • The `post` field accepts up to 3,000 characters.
    • LinkedIn will automatically preview the link in the post unless there is an image or video included. In the above example the image will show. Removing the image will cause the link preview to show. If the link preview cannot be retrieved, the post will still be published and a `linkPreviewFailed: true` field will be returned in the /post response.
    • Hashtags are clickable in LinkedIn. Use the # symbol to specify a LinkedIn hashtag.
    • If publishing a video and doesn't end in a known video extension such as mp4, please use the `isVideo` parameter. See the [/post endpoint](/docs/apis/post/post) for details.
    • LinkedIn video processing takes several minutes to complete. Please wait before trying the share URL or getting video analytics.
    • LinkedIn limits 150 posts per day per LinkedIn account.
    • Posting to LinkedIn Groups is not supported.
    • LinkedIn also supports sending media without post text. If you do not want post text included send an empty String `post: ""`.
    • You may link either a company page or a personal LinkedIn account.
    • See [LinkedIn Media Guidelines](/docs/media-guidelines/linkedin) and [LinkedIn Authorization](/docs/dashboard/connect-social-accounts/linkedin) for more information.
    > [!WARNING] > > You must be a "Super Admin" or a "Content Admin" to connect a LinkedIn company page. ## Posting to LinkedIn JSON for a basic post with a link and image to LinkedIn: ```json LinkedIn Post theme={"system"} { "post": "The best LinkedIn post ever #best https://www.linkedin.com", // empty string is allowed "mediaUrls": ["https://img.ayrshare.com/012/gb.jpg"], "platforms": ["linkedin"] } ``` ## LinkedIn Options You can set additional options for a post by using the `linkedInOptions` parameter. ```json LinkedIn Options theme={"system"} { "linkedInOptions": { "altText": ["This is my best pic", "😃 here is the next one"], "disableShare": true, "targeting": { "countries": ["US", "IN", "DE", "GB"], "seniorities": ["Senior", "VP"], "degrees": ["Master of Digital Design", "Bachelor of Engineering"], "fieldsOfStudy": ["Consumer Economics", "Computer Games and Programming Skills"], "industries": ["Telecommunications Carriers", "Banking"], "jobFunctions": ["Information Technology", "Entrepreneurship"], "staffCountRanges": ["size_51_to_200", "size_5001_to_10000"] }, "thumbNail": "https://example.com/thumbnail.jpg", "title": "Sample PPTX", "titles": ["This is an amazing video", "😃 here is the next one"], "visibility": "public" } } ``` LinkedIn options are optional fields that can be used to control the post. **altText** `array of strings` The alternative text of images for accessibility and screen readers. See [LinkedIn Alternative Text](/docs/apis/post/social-networks/linkedin#alternative-text) for more information. **disableShare** `boolean` (default: false) Disable the ability for users to reshare the LinkedIn post. See [LinkedIn Disable Share](/docs/apis/post/social-networks/linkedin#disable-share) for more information. **targeting** `object` Target your organic posts to specific groups by countries, industry, job title, and more. Requires at least 300 followers in the targeted audience. See [LinkedIn Audience Targeting](/docs/apis/post/social-networks/linkedin#audience-targeting) for more information. **thumbNail** `string` The thumbnail of a video. Must be PNG or JPG, same dimensions as video, under 10MB. URL should end in .png or .jpg. See [LinkedIn Video Thumbnail](/docs/apis/post/social-networks/linkedin#video-thumbnail) for more information. **title** `string` The title of a document when posting PPT, PPTX, DOC, DOCX, or PDF files. Maximum length: 400 characters. If not specified, the filename is used. See [LinkedIn Documents](/docs/apis/post/social-networks/linkedin#documents) for more information. **titles** `array of strings` Title or media captions to LinkedIn images or videos. Each title corresponds to a media URL in order. See [LinkedIn Media Titles](/docs/apis/post/social-networks/linkedin#media-titles) for more information. **visibility** `string` (default: public) The visibility of the post. Values: `public`, `connections`, or `loggedin`. See [LinkedIn Post Visibility](/docs/apis/post/social-networks/linkedin#post-visibility) for more information. ## Alternative Text Add LinkedIn alternative text, also known as LinkedIn alt text, to a LinkedIn image. LinkedIn alt text is an accessibility feature used for additional user info and screen readers. LinkedIn does not support altText on videos or documents. Use the `altText` in the `linkedInOptions` object. ```json LinkedIn Alt Text theme={"system"} { "linkedInOptions": { "altText": ["This is my best pic", "😃 here is the next one"] // Array of Alt Texts } } ``` Each alt text must correspond to an image in the `mediaUrls` array. The alt text will be applied to each image in order. ## Audience Targeting LinkedIn allows you to target your organic posts to specific groups of users. You can target by countries, industry, job title, and more. > [!WARNING] > > LinkedIn targeting requires at least **300 followers in the targeted audience**. For example if > you have 400 followers and target just users in the country US, but only 200 of your followers are > in the US, the targeting will fail since the audience is too small. Use the `targeting` object in the `linkedInOptions` object: ```json LinkedIn Audience Targeting theme={"system"} { "linkedInOptions": { "targeting": { "countries": ["US", "IN", "DE", "GB"], "seniorities": ["Senior", "VP"], "degrees": ["Master of Digital Design", "Bachelor of Engineering"], "fieldsOfStudy": ["Consumer Economics", "Computer Games and Programming Skills"], "industries": ["Telecommunications Carriers", "Banking"], "jobFunctions": ["Information Technology", "Entrepreneurship"], "staffCountRanges": ["size_51_to_200", "size_5001_to_10000"] } } } ``` The available targeting options are listed in the following JSON files:
    • [LinkedIn Countries](https://logo.ayrshare.com/data/linkedin/countries.json)
    • [LinkedIn Seniorities](https://logo.ayrshare.com/data/linkedin/seniorities.json)
    • [LinkedIn Degrees](https://logo.ayrshare.com/data/linkedin/degrees.json)
    • [LinkedIn Fields of Study](https://logo.ayrshare.com/data/linkedin/fieldsOfStudy.json)
    • [LinkedIn Industries](https://logo.ayrshare.com/data/linkedin/industries.json)
    • [LinkedIn Job Functions](https://logo.ayrshare.com/data/linkedin/jobFunctions.json)
    • [LinkedIn Staff Count Ranges](https://logo.ayrshare.com/data/linkedin/staffCountRanges.json)
    ## Authorization Refresh LinkedIn must be reauthorized *every year* via the Social Accounts page.
    • An email and webhook social action notification will be sent 15 days in advance of authorization expiration.
    • The refresh required date and remaining days can be retrieved from the [/user](/docs/apis/user/overview) endpoint.
    • The user will see on their social account linkage page an alert and a refresh button 30 days prior to expiration.
    ## Character Limits Please see [LinkedIn Character Limits](/docs/help-center/technical-support/character-limits#linkedin-character-limits) for more information. ## Disable Share You can disable the ability for users to reshare the LinkedIn post by setting the `disableShare` field to `true` in the `linkedInOptions` object: ```json LinkedIn Disable Share theme={"system"} { "linkedInOptions": { "disableShare": true } } ``` ## Documents Use the API to post a document on LinkedIn. Supported file formats include: PPT, PPTX, DOC, DOCX, and PDF. Ensure the document is under 100MB in size and does not exceed 300 pages. Below is a code sample demonstrating how to post a PPTX document with a title: ```json LinkedIn Document Post theme={"system"} { "post": "What a great document", "platforms": ["linkedin"], "linkedInOptions": { "title": "Sample PPTX" // optional. If not specified, the file name used. }, "mediaUrls": ["https://scholar.harvard.edu/files/torman_personal/files/samplepptx.pptx"] } ``` The media URL must point to a file with a known document extension: ppt, pptx, doc, docx, or pdf. A query string after the file name is fine, so signed URLs such as Firebase Storage or presigned S3 links work, for example `https://example.com/files/deck.pdf?token=abc`. ## LinkedIn Mentions You may mention other LinkedIn organizations, i.e. company pages, or member profiles, i.e. individual profile pages, in your posts. Ayrshare makes an attempt to resolve the @mention by doing a lookup on the handle. If the @mention is not found, the @ will be removed from the post since LinkedIn will not accept posts with unresolved mentions. Individual LinkedIn profiles cannot be mentioned. > [!WARNING] > > If any mention in a LinkedIn post cannot be resolved (due to privacy settings, > account restrictions, or other platform limitations), LinkedIn may fail to > render all mentions in that post as clickable links. This is LinkedIn's platform > behavior and not an API issue. To avoid this, ensure all mentioned users and > organizations allow mentions from your organization or consider posting mentions > separately to isolate any problematic mentions. Please review the [important rules](/docs/testing/post-verification#mentions) on mentions. ### Organization Profiles Mention another LinkedIn organization handle by adding `@handle` in the post text. The `@handle` is the name of the company or organization. You can mention in posts, comments, and reply to comments. For example,
    • Ayrshare's LinkedIn company profile is [https://www.linkedin.com/company/ayrshare](https://www.linkedin.com/company/ayrshare) and the handle used would be *@ayrshare*.
    • Zara's LinkedIn company profile is [https://www.linkedin.com/company/zara-sa/](https://www.linkedin.com/company/zara-sa/) and the handle used would be *@zara-sa*.
    ```json LinkedIn Mention theme={"system"} { "post": "The best social media API @ayrshare ever!", "platforms": ["linkedin"] } ``` ### Member Profiles Mention another LinkedIn member profile by adding `@vanity_name` or `@[John Smith]` in the post text. 1. `@vanity_name` is the vanity name of the member profile. For example in the member's profile URL `https://www.linkedin.com/in/vanity_name/`, the vanity name is `vanity_name`. 2. `@[John Smith]` is the person's full name. For example if the person's full name is `John Smith`, the mention would be `@[John Smith]`. You can mention in posts, comments, and reply to comments. ```json LinkedIn Mention theme={"system"} { "post": "The best social media API @[John Smith] and @ava_smith ever!", "platforms": ["linkedin"] } ``` In the above example, John Smith and Ava Smith will be mentioned in the post - the first using their full name and the second using their vanity name. **Important Notes:** 1. You must have a LinkedIn company page connected to your Ayrshare account to mention member profiles. If you connect your own personal LinkedIn account, you will not be able to mention other people's profiles. 2. LinkedIn only allows mentioning people who follow your organization / company page. ## Media Titles Add title or media captions to LinkedIn images or videos. Use the `titles` in the `linkedInOptions` object. ```json LinkedIn Media Titles theme={"system"} { "linkedInOptions": { "titles": ["This is an amazing video", "😃 here is the next one"] // Array of Alt Texts } } ``` Each title must correspond to an image or video in the `mediaUrls` array. The title will be applied to each image or video in order. ## Multi Image Posts You may post up to *9 image URLs* in the `mediaUrls` to LinkedIn. ```json LinkedIn Multi Image Post theme={"system"} { "mediaUrls": ["https://url1", ..., "https://url9"] } ``` LinkedIn does not yet support carousel images for organic posts. ## Post Visibility LinkedIn allows you to control the visibility of your posts. You can set the visibility to public, connections only, or loggedin users only. If no visibility is specified, the post will be "public" and visible to all LinkedIn users.
    • `public`: The post will be visible to all LinkedIn users. Available for both company pages and personal LinkedIn connected accounts. Default if no visibility is specified.
    • `connections`: The post will be visible to the 1st degree network of the owner (user profile). Available for personal LinkedIn connected accounts only.
    • `loggedin`: The post will be visible to logged in LinkedIn users only. Available for both company pages and personal LinkedIn connected accounts.
    Use the `visibility` field in the `linkedInOptions` object: ```json LinkedIn Post Visibility theme={"system"} { "linkedInOptions": { "visibility": "public" // "public", "connections", or "loggedin" } } ``` ## Video Publishing Permissions On LinkedIn, only users with specific permissions can publish videos to company pages: 1. Users with Admin permissions. 2. Users with Direct Sponsored Content (DSC) permissions. Content Administrators do not have the ability to publish videos to LinkedIn company pages. ## Video Thumbnail Set a LinkedIn thumbnail for a video. Send a remote URL of a PNG or JPG file that is the same dimensions as the video and less than 10 MB. URL should end in .png or .jpg. Use the `thumbNail` in the `linkedInOptions` object. ```json LinkedIn Video Thumbnail theme={"system"} { "linkedInOptions": { "thumbNail": "https://octodex.github.com/images/Fintechtocat.png" } } ``` Please see here for more [examples of using the LinkedIn API](https://www.ayrshare.com/how-to-post-and-get-analytics-with-the-linkedin-api#linkedin-api-examples). # Supported Social Networks Source: https://www.ayrshare.com/docs/apis/post/social-networks/overview Compare the 13 networks the /post endpoint publishes to, with character, image, and video limits for each and an example that posts to several at once. The Ayrshare [/post endpoint](/docs/apis/post/post) publishes to every social network below from a single API call. Add one or more networks to the `platforms` array and Ayrshare handles the network-specific formatting, media conversion, and delivery. Each network supports its own publishing options, media types, and limits. Select a network below for its full reference. ## Supported Social Networks > [card] [Bluesky](/docs/apis/post/social-networks/bluesky) > > Text posts with up to 4 images or 1 video, alt text, and animated GIFs. > [card] [Facebook](/docs/apis/post/social-networks/facebook) > > Page posts, Reels, image carousels, and automatic link previews. > [card] [Google Business Profile](/docs/apis/post/social-networks/google) > > Standard What's New posts, events, and offers, plus single image or video uploads. > [card] [Instagram](/docs/apis/post/social-networks/instagram) > > Feed posts, Reels, Stories, and carousels of up to 10 images or videos. > [card] [LinkedIn](/docs/apis/post/social-networks/linkedin) > > Personal and company pages, documents (PDF/PPT/DOC), and up to 9 images. > [card] [Pinterest](/docs/apis/post/social-networks/pinterest) > > Image and video Pins, carousels of up to 5 images, and board selection. > [card] [Reddit](/docs/apis/post/social-networks/reddit) > > Submit image or text posts to any subreddit you have access to. > [card] [Snapchat](/docs/apis/post/social-networks/snapchat) > > Stories and Spotlight Snaps with a single image or video per post. > [card] [Telegram](/docs/apis/post/social-networks/telegram) > > Channel and group posts with an image or video, animated GIFs, and link previews. > [card] [Threads](/docs/apis/post/social-networks/threads) > > Text posts and carousels of up to 20 combined images or videos. > [card] [TikTok](/docs/apis/post/social-networks/tiktok) > > Direct video publishing plus photo posts of up to 35 images. > [card] [X (Twitter)](/docs/apis/post/social-networks/x-twitter) > > Tweets with up to 4 images or 1 video, long video, and BYO credentials. > [card] [YouTube](/docs/apis/post/social-networks/youtube) > > Publish videos with a title, description, tags, visibility, and playlists. ## Posting at a Glance A quick reference for the most common publishing limits. See [Character Limits](/docs/help-center/technical-support/character-limits) and [Media Requirements](/docs/media-guidelines/overview) for the complete, per-network reference. | Network | Post Characters | Max Images | Video | | - | - | - | - | | [Bluesky](/docs/apis/post/social-networks/bluesky) | 300 | 4 | 1 | | [Facebook](/docs/apis/post/social-networks/facebook) | 63,206 | 10 | 1 | | [Google Business Profile](/docs/apis/post/social-networks/google) | 1,500 | 1 | 1 | | [Instagram](/docs/apis/post/social-networks/instagram) | 2,200 | 10 | 1 | | [LinkedIn](/docs/apis/post/social-networks/linkedin) | 3,000 | 9 | 1 | | [Pinterest](/docs/apis/post/social-networks/pinterest) | 500 | 5 | 1 | | [Reddit](/docs/apis/post/social-networks/reddit) | 5,000 | 1 | Not yet supported | | [Snapchat](/docs/apis/post/social-networks/snapchat) | 500 | 1 | 1 | | [Telegram](/docs/apis/post/social-networks/telegram) | 1,024 | 1 | 1 | | [Threads](/docs/apis/post/social-networks/threads) | 500 | 20 | 1 | | [TikTok](/docs/apis/post/social-networks/tiktok) | 2,200 | 35 | 1 | | [X (Twitter)](/docs/apis/post/social-networks/x-twitter) | 280 (25,000 Premium) | 4 | 1 | | [YouTube](/docs/apis/post/social-networks/youtube) | 5,000 | Video only | 1 | > [!NOTE] > > Most networks accept **either** multiple images **or** a single video in one post, not both. Instagram and Threads are the > exceptions: their carousels may combine images and videos (up to 10 and 20 items respectively). See each network's page and the > [Media Requirements](/docs/media-guidelines/overview) for the exact rules. ## Posting to Multiple Networks Publish the same content everywhere by listing each network in the `platforms` array. Network-specific options are passed in their own object (for example, `instagramOptions` or `youTubeOptions`) and are ignored by the other networks. ```json Post to Multiple Networks theme={"system"} { "post": "Launch day is here! 🚀", "platforms": ["bluesky", "facebook", "instagram", "linkedin", "twitter"], "mediaUrls": ["https://img.ayrshare.com/012/gb.jpg"] } ``` > [!NOTE] > > Each network must first be linked to the user profile. See [Connect Social Accounts](/docs/dashboard/connect-social-accounts/overview) > to authorize the networks you want to publish to. # Pinterest API Source: https://www.ayrshare.com/docs/apis/post/social-networks/pinterest Create Pinterest image pins, carousels, and video pins with board ID, pin title, pin link, pin note, alternative text, and Pinterest AI disclosures. ## Posting an Image Pin to Pinterest The Pinterest API requires an image to be included in the post. Other parameters are optional. Please see [Pinterest Media Guidelines](/docs/media-guidelines/pinterest) and [Pinterest Authorization](/docs/dashboard/connect-social-accounts/pinterest) for more information. ```json Pinterest Post theme={"system"} { "post": "The best Pinterest API ever!", // Maximum 500 characters or empty string "platforms": ["pinterest"], "mediaUrls": ["https://images.ayrshare.com/imgs/GhostBusters.jpg"] } ``` ## Pinterest Options You can set additional options for a post by using the `pinterestOptions` parameter. ```json Pinterest Post theme={"system"} { "post": "The best Pinterest API ever!", // Maximum 500 characters or empty string "platforms": ["pinterest"], "mediaUrls": ["https://images.ayrshare.com/imgs/GhostBusters.jpg"], "pinterestOptions": { "altText": "This is my best pic", // maximum 500 characters "boardId": "93383204522", "carouselOptions": [ { "title": "Image 1", "link": "https://www.cnn.com", "description": "Super Image 1" }, { "title": "Image 2", "link": "https://www.google.com", "description": "Super Image 2" } ], "link": "https://www.ayrshare.com", // maximum 2048 characters "note": "Private note", // maximum 1000 characters "aiDisclosures": ["AI_MODIFIED"], "thumbNail": "https://images.ayrshare.com/imgs/GhostBusters.jpg", "title": "A Pinterest Board for You" // maximum 100 characters } } ``` Pinterest options are optional fields that can be used to control the post. **altText** `string` The alternative text for images and videos for accessibility and screen readers. Maximum 500 characters. If you send an array, only the first item is used. See [Pinterest Alternative Text](/docs/apis/post/social-networks/pinterest#alternative-text) for more information. **aiDisclosures** `array of strings` Declare AI disclosures for the whole Pin. Accepted values are `AI_MODIFIED` and `SYNTHETIC_PERFORMER`. See [Pinterest AI Disclosures](/docs/apis/post/social-networks/pinterest#pinterest-ai-disclosures) for more information. **boardId** `string` Post to another one of the user's Pinterest Boards by specifying the ID from the /user/details endpoint. If not specified, posts to the default linked board. See [Pinterest Board ID](/docs/apis/post/social-networks/pinterest#board-id) for more information. **carouselOptions** `array of objects` Add up to 5 images as a Pinterest carousel with optional titles, links, and descriptions. Each object can contain: `title`, `link`, and `description`. See [Pinterest Image Carousel](/docs/apis/post/social-networks/pinterest#pinterest-image-carousel) for more information. **link** `string` The destination URL users will be directed to when clicking the pin image. Maximum 2048 characters. Creates a clickable link attached to your pin. See [Pin Link](/docs/apis/post/social-networks/pinterest#pin-link) for more information. **note** `string` Add private notes to individual Pins that only you and board collaborators can see. Maximum 1000 characters. See [Pin Note](/docs/apis/post/social-networks/pinterest#pin-note) for more information. **thumbNail** `string` Set a thumbnail for video pins. Must be PNG or JPG, same dimensions as video, under 10MB. Required for video pins. See [Video Pin (Thumbnail)](/docs/apis/post/social-networks/pinterest#video-pin-thumbnail) for more information. **title** `string` The title of the pin to display in the "Add your title" section. Maximum 100 characters. See [Pin Title](/docs/apis/post/social-networks/pinterest#pin-title) for more information. \*Links in the `post` field are not clickable. Please use the `link` field to add a clickable link to the Pin. See [Pinterest Media Guidelines](/docs/media-guidelines/pinterest) for more information. ## Alternative Text Add alternative text, also known as alt text, to a Pinterest image or video. Pinterest alt text is an accessibility feature used for additional user info and screen readers. Use the `altText` in the `pinterestOptions` object. ```json Pinterest Alt Text theme={"system"} { "pinterestOptions": { "altText": "This is my best pic" } } ``` The alt text is limited to 500 characters. ## Pinterest AI Disclosures Use `pinterestOptions.aiDisclosures` to send Pinterest's AI disclosure declarations when creating a Pin. The disclosures apply to the whole Pin, including image carousels, and are supported for image Pins, carousel Pins, and video Pins. ```json Pinterest AI Disclosure theme={"system"} { "post": "An AI-assisted concept for a garden studio", "platforms": ["pinterest"], "mediaUrls": ["https://example.com/garden-studio.jpg"], "pinterestOptions": { "aiDisclosures": ["AI_MODIFIED"] } } ``` You can send both accepted values together: ```json Pinterest AI Disclosure theme={"system"} { "pinterestOptions": { "aiDisclosures": ["AI_MODIFIED", "SYNTHETIC_PERFORMER"] } } ``` Accepted values:
    • `AI_MODIFIED`: The Pin contains AI-modified content.
    • `SYNTHETIC_PERFORMER`: The Pin contains a synthetic performer.
    If `aiDisclosures` is omitted or an empty array, Ayrshare does not send Pinterest an AI disclosure field. Duplicate values are sent once. Values are case-sensitive and are not coerced. If the option is invalid, for example an unknown value, wrong casing, a non-array value, or an array containing a non-string entry, the Pin can still publish successfully, but Ayrshare omits the entire disclosure option and returns a nonfatal warning on the Pinterest result. Correct the request and send one or both accepted values if the Pin needs an explicit AI disclosure. Pinterest may still apply its own labels independently. The MCP server and the GraphQL API check value types first. On those interfaces, a malformed value such as a list containing a number is rejected before posting, instead of publishing with a warning. Unknown or wrongly cased strings still publish with the warning. ## Board ID Post to another one of the user's Pinterest Boards by specifying the ID obtained from the [/user/details](/docs/apis/user/pinterest-board) endpoint. Otherwise post to the default linked board. Use the `boardId` in the `pinterestOptions` object. ```json Pinterest Board ID theme={"system"} { "pinterestOptions": { "boardId": "93383204522" } } ``` ## Character Limits Please see [Pinterest Character Limits](/docs/help-center/technical-support/character-limits#pinterest-character-limits) for more information. ## Pin Link The destination URL that users will be directed to when they click on your pin image. This creates a clickable link attached to your pin. Maximum length is 2048 characters. Use the `link` in the `pinterestOptions` object. ```json Pin Link theme={"system"} { "pinterestOptions": { "link": "https://www.ayrshare.com" } } ``` ## Pin Note You can add private notes to individual Pins on your boards. Only you and board collaborators will be able to see them. Use the `note` in the `pinterestOptions` object. ```json Pin Note theme={"system"} { "pinterestOptions": { "note": "Private note for collaborators" } } ``` ## Pinterest Image Carousel Post up to five images as a Pinterest carousel. By adding more than one media URL, a carousel is automatically created. You can also add in optional carousel parameter. Please see below. ```json Pinterest Carousel Post theme={"system"} { "post": "Carousel Time", "platforms": ["pinterest"], "mediaUrls": [ "https://img.ayrshare.com/random/photo-1.jpg", "https://img.ayrshare.com/random/photo-2.jpg" ], "pinterestOptions": { "carouselOptions": [ // optional { "title": "Image 1", "link": "https://www.cnn.com", "description": "Super Image 1" }, { "title": "Image 2", "link": "https://www.google.com", "description": "Super Image 2" } ] } } ``` The optional `carouselOptions` field takes an array of objects. Each carousel object corresponds to the equivalent media URL string, e.g. the first carousel object refers to the first `mediaUrl` string.
    • `title`: The image title.
    • `link`: The external destination link for the image.
    • `description`: The image description.
    ## Pinterest Mentions While you can add a @handle to a Pinterest post, Pinterest does not support resolving mentions in the post text. The @handle will remain as plain text. ## Pin Title The title of the pin to display. This appears in the "Add your title" section of a new pin. Maximum 100 characters. Use the `title` in the `pinterestOptions` object. ```json Pin Title theme={"system"} { "pinterestOptions": { "title": "A Pinterest Board for You" } } ``` ## Video Pin (Thumbnail) A video may be posted as a Pin using the standard `mediaUrls` field in the [/post endpoint](/docs/apis/post/post). Additionally, a thumbnail image URL **must be included** with the video with a valid media Content-Type such as `image/jpeg`. Please see [requirements](/docs/media-guidelines/pinterest). ```json Video Pin with Thumbnail theme={"system"} { "pinterestOptions": { // required "thumbNail": "https://images.unsplash.com/photo-1513093496871-0a81425386e5?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxNjQ1ODN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2Mzg0ODAzNDA&ixlib=rb-1.2.1&q=80&w=400" } } ``` Additional information on [posting Pins using the API](https://www.ayrshare.com/blog/pinterest-api-integration-on-ayrshare/). If your video doesn't end in a known video extension such as mp4, please use the `isVideo` parameter. See the [/post endpoint](/docs/apis/post/post) for details. # Reddit API Source: https://www.ayrshare.com/docs/apis/post/social-networks/reddit Post text, links, and images to a subreddit with the Reddit API, add Reddit flair, check if a subreddit exists, add rich text, and use Reddit mentions. ## Posting to Reddit The following are required body parameters for posting using the Reddit API. Please be sure the subreddit allows automated posting. Please see [Reddit Media Guidelines](/docs/media-guidelines/reddit) and [Reddit Authorization](/docs/dashboard/connect-social-accounts/reddit) for more information. ```json Reddit Post theme={"system"} { "post": "Reddit post", // empty string is allowed "platforms": ["reddit"], // required "redditOptions": { "title": "Reddit Post Title", // required "subreddit": "test", // required (no "/r/" needed) "link": "https://www.website.com" // optional: post a link } } ```
    • `title` (required): The title of the post on Reddit.
    • `subreddit` (required): The subreddit to send the post. Please be sure to follow the subreddit's posting guidelines. A good subreddit to use for testing is */r/test/* and can be viewed at: [https://www.reddit.com/r/test/](https://www.reddit.com/r/test/)
    • `link` (optional): If posting to Reddit and you want to post a link instead of text to the subreddit. While the `post` parameter text is still required, it is not included in the post since Reddit links can't have a text body.
    > [!WARNING] > > Always check the rules of the subreddit before posting. Some subreddits have a karma threshold, > require membership for a time period, limit the frequency of posting, don't allow images, or > require flair to be added. Also, most subreddits have specific rules on spam and self-promotion. > Not following these rules can result in being banned by the subreddit or Reddit. ## Posting an Image to Reddit Post an image to Reddit either by including the image URL in the `mediaUrl` parameter or the `link` parameter of `redditOptions`. Images posted as a `link` will resolve to the full image. The `post` parameter text is ignored, but is still required to successfully submit the post. Reddit restricts posting images and text together to their online "fancy pants" editor. This means either an image or text can be posted, but not both at the same time. ```json Reddit Post with Image theme={"system"} { "post": "Reddit post", // required, but not added to the post "platforms": ["reddit"], // required "mediaUrls": "https://my-media-url.com", "redditOptions": { "title": "Reddit Post Title", // required "subreddit": "test" // required (no "/r/" needed) } } ``` ## Add Reddit Flair Some subreddits require flair. Subreddit moderators pre-define the flair and each flair has a unique ID. In the Reddit post to Ayrshare add the flair ID and an optional flair text if the text can be overridden. ```json Reddit Post with Flair theme={"system"} { "redditOptions": { "flairId": "jM8nH92enjswas", // Id of the flair "flairText": "My New Flair text" // Override the flair text if allowed by flair } } ``` You can find the flair ID of a subreddit by calling the [/post/redditFlair](/docs/apis/utils/reddit-get-flair) endpoint. ## Check if a Subreddit Exists Check if a subreddit exists with the [/validate endpoint](/docs/apis/validate/check-subreddit). ## Add rich text formatting to Reddit posts If you want to add bold, italic, or superscript text to a Reddit post, use [Reddit-flavored Markdown](https://www.reddit.com/wiki/markdown#wiki_new_reddit-flavored_markdown). ```json Reddit Post with Rich Text theme={"system"} { "post": "For example, this can be *italic* or **bold** or ^super ", "platforms": ["reddit"], "redditOptions": { "title": "Rich Text Post", "subreddit": "test" } } ``` ## Reddit Mentions Mention another Reddit user or subreddit.
    • Mention a Reddit user by adding `@subreddit` or `u/username` in the post text.
    • Mention a subreddit by adding `r/subreddit` in the post text.
    For example: ```json Reddit Post with Mention theme={"system"} { "post": "The best Reddit post ever for user @ayrshare and subreddit r/SiliconValleyHBO", "platforms": ["reddit"] } ``` > [!WARNING] > > Please review the [important rules](/docs/testing/post-verification#mentions) on mentions. ## Character Limits Please see [Reddit Character Limits](/docs/help-center/technical-support/character-limits#reddit-character-limits) for more information. ## Additional Endpoints > [card] [Get Reddit Flair](/docs/apis/utils/reddit-get-flair) # Snapchat API Source: https://www.ayrshare.com/docs/apis/post/social-networks/snapchat Publish to Snapchat Stories, Saved Stories, and Spotlight with the Snapchat API, plus how Snapchat mentions, hashtags, and character limits are handled. > [!NOTE] > > When Ayrshare converts a WebP, HEIC, HEIF, or AVIF image to JPEG for Snapchat it preserves the image's color rendering and writes a small XMP packet carrying only the `Iptc4xmpExt:DigitalSourceType` AI disclosure, when the image declares one. No other metadata is carried over: not EXIF, and nothing else from the original XMP, so a location, a creator name or a copyright line can't be published by accident. The C2PA cryptographic signature > does not survive a re-encode. See [Image Metadata and Content > Credentials](/docs/media-guidelines/overview#image-metadata-and-content-credentials). ## Posting to Snapchat The Snapchat API enables direct publishing of content to both Stories and Spotlight. Stories are temporary posts that disappear after 24 hours, while Spotlight posts are permanent and can help creators and businesses reach a wider audience. There are three types of posts you can create on Snapchat:
    • [Stories](/docs/apis/post/social-networks/snapchat#stories)
    • [Saved Stories](/docs/apis/post/social-networks/snapchat#saved-stories)
    • [Spotlight](/docs/apis/post/social-networks/snapchat#spotlight)
    Snapchat accepts either video or image media. Only one media item is allowed per post. You can learn more using Snapchat's API:
    • [Snapchat Media Guidelines](/docs/media-guidelines/snapchat) and [Snapchat Authorization](/docs/dashboard/connect-social-accounts/snapchat).
    • [Snapchat API integration guide](https://www.ayrshare.com/blog/complete-guide-to-snapchat-api-integration/) to learn more about how to post or get analytics on Snapchat.
    ## Stories Stories are ephemeral Snaps viewable by friends, subscribers, and non-subscribers in the Snapchat app's 4th tab Discover feed for up to 24-hours. Publish a Snapchat story as you would any other post: ```json Publish a Snapchat story theme={"system"} { "post": "An amazing story", // empty string is allowed "platforms": ["snapchat"], "mediaUrl": ["https://img.ayrshare.com/video.mp4"] } ``` ## Saved Stories Saved Stories are permanent Stories that are viewable on a Public Profile indefinitely. Publish a Snapchat story with `snapChatOptions` and the `savedStory` option: ```json Publish a Snapchat saved story theme={"system"} { "post": "An amazing saved story", "platforms": ["snapchat"], "mediaUrl": ["https://img.ayrshare.com/video.mp4"], "snapChatOptions": { "savedStory": true } } ``` ## Spotlight Spotlights are permanent video Snaps that are distributed and viewable on Spotlight, Snapchat's entertainment platform for user-generated content. Spotlight offers a channel for creators and businesses to grow their audience on Snapchat. Publish a Snapchat story with `snapChatOptions` and the `spotlight` option: ```json Publish a Snapchat spotlight theme={"system"} { "post": "An amazing spotlight", "platforms": ["snapchat"], "mediaUrl": ["https://img.ayrshare.com/video.mp4"], "snapChatOptions": { "spotlight": true } } ``` ## Snapchat Mentions Currently, user mentions (@username) in Spotlight posts don't resolve as interactive mentions through the API. The text will appear as plain text in your Spotlight posts rather than creating clickable user links. ```json Example with mentions (displays as plain text) theme={"system"} { "post": "Check out this awesome content from @johndoe and @janedoe!", "platforms": ["snapchat"], "mediaUrl": ["https://img.ayrshare.com/image.jpg"], "snapChatOptions": { "spotlight": true } } ``` ## Snapchat Hashtags Hashtags are fully supported in Spotlight posts and help increase discoverability. You can include multiple hashtags in your post content to categorize your content and reach relevant audiences. Hashtags are also clickable. ```json Example with hashtags theme={"system"} { "post": "Just launched our new product! 🚀 #ayrshare #tech #exciting", "platforms": ["snapchat"], "mediaUrl": ["https://img.ayrshare.com/product-launch.mp4"], "snapChatOptions": { "spotlight": true } } ``` ## Character Limits Please see [Snapchat Character Limits](/docs/help-center/technical-support/character-limits#snapchat-character-limits) for more information. # Telegram API Source: https://www.ayrshare.com/docs/apis/post/social-networks/telegram Send Telegram posts with links, images, and animated GIFs using the Telegram API, plus how Telegram mentions work and the character limits for each post. ## Posting to Telegram JSON for a basic post with a link and image using the Telegram API: ```json Telegram Post theme={"system"} { "post": "The best Telegram message ever #best https://www.telegram.com", // empty string is allowed "mediaUrls": ["https://img.ayrshare.com/012/gb.jpg"], "platforms": ["telegram"] } ``` Telegram will automatically preview the link in the channel or group unless there is an image or video included. In the above example the image will show. Removing the image will cause the link preview to show. If your video doesn't end in a known video extension such as mp4, please use the `isVideo` parameter. See the [/post endpoint](/docs/apis/post/post) for details. See [Telegram Media Guidelines](/docs/media-guidelines/telegram) and [Telegram Authorization](/docs/dashboard/connect-social-accounts/telegram) for more information. ## Animated GIFs Only one URL is allowed with a Telegram animated GIF. If the media URL does not end in ".gif" or ".GIF", set the `isVideo` field to `true`. ```json Telegram Animated GIF theme={"system"} { "randomPost": true, "platforms": [ "telegram" ], "isVideo": true, // Set to true if the mediaURL does not end in .gif or .GIF "mediaUrls": ["https://img.ayrshare.com/012/cat.gif"] } ``` ## Telegram Mentions Mention another Telegram handle by adding `@handle` in the post text. For example: ```json Telegram Post with Mention theme={"system"} { "post": "The best Telegram image post ever @handle", "platforms": ["telegram"] } ``` > [!WARNING] > > Please review the [important rules](/docs/testing/post-verification#mentions) on mentions. ## Character Limits Please see [Telegram Character Limits](/docs/help-center/technical-support/character-limits#telegram-character-limits) for more information. # Threads API Source: https://www.ayrshare.com/docs/apis/post/social-networks/threads Post to Threads with image and video carousels, geo restrictions, Threads mentions, and multi-post threads with thread media, thread breaks, and deletion. > [!NOTE] > > Threads publishing fetches media from your URL via Meta's server-side crawler. > If you see Ayrshare error code 379 — especially alongside an Instagram error 440 or 138 in the same > publish — see [Meta Media Crawler Blocked](/docs/help-center/technical-support/meta-media-crawler-blocked). > [!NOTE] > > Posting an AI-generated image? When Ayrshare converts a WebP, HEIC, HEIF, or AVIF image to JPEG it > writes a small XMP packet carrying only the `Iptc4xmpExt:DigitalSourceType` AI disclosure, when the image declares one. Threads stores the disclosure but does not render an AI label > today. No other metadata is carried over: not EXIF, and nothing else from the original XMP, so a location, a creator name or a copyright line can't be published by accident. The C2PA cryptographic signature does not survive a re-encode. See [Image > Metadata and Content Credentials](/docs/media-guidelines/overview#image-metadata-and-content-credentials). ## Posting to Threads JSON for a basic post with a link and image using the Threads API: ```json Threads Post theme={"system"} { "post": "The best Threads post ever #best #awesome https://www.threads.net", // empty string is allowed "mediaUrls": ["https://img.ayrshare.com/012/gb.jpg"], "platforms": ["threads"] } ``` The Threads API has the following requirements and restrictions:
    • Threads will automatically preview the link in the post unless there is an image or video included. In the above example the image will show. Removing the image will cause the link preview to show.
    • Threads profiles are limited to 250 API-published posts within a 24-hour moving period.
    • Threads only allows 1 hashtag per post.
    • @mention Threads users will receive a notification.
    • Maximum 500 post characters.
    • Multi-image/videos posts are supported and sent as a carousel. You may send up to 20 videos and images.
    • Published Threads posts can be deleted with the [delete endpoint](/docs/apis/post/delete-post).
    • If your video doesn't end in a known video extension such as mp4, please use the `isVideo` parameter. See the [/post endpoint](/docs/apis/post/post) for details.
    • Threads also supports sending media without post text. If you do not want post text included send an empty String `post: ""`.
    • See [Threads Media Guidelines](/docs/media-guidelines/threads) and [Threads Authorization](/docs/dashboard/connect-social-accounts/threads) for more information.
    ## Threads Options You can set additional options for a post by using the `threadsOptions` parameter. ```json Threads Options theme={"system"} { "threadsOptions": { "allowCountries": ["US", "CA"], "thread": true, "threadNumber": true, "mediaUrls": ["https://img.ayrshare.com/012/gb.jpg"] // used when sending as a thread of threads } } ``` Threads options are optional fields that can be used to control the post. **allowCountries** `array of strings` Restrict posts to specific countries using country codes. Use [country codes](/docs/iso-codes/country). Only available if Meta has enabled geo restrictions for your account. See [Threads Geo Restrictions](/docs/apis/post/social-networks/threads#geo-restrictions) for more information. **thread** `boolean` (default: false) Break long posts into a connected thread series with optional numbering and media. See [Thread](/docs/apis/post/social-networks/threads#thread) for more information. **threadNumber** `boolean` (default: false) Automatically add numbers at the end of each thread in the format of 1/n. Requires `thread: true`. **mediaUrls** `array of strings` Add media objects to a thread of threads. One media object will be added to each thread in order. Use `null` to skip media for a specific thread. Use objects with multiple URLs for multiple media per thread. See [Thread Media](/docs/apis/post/social-networks/threads#thread-media) for more information. ## Adding line breaks or rich text to a Threads post Threads line breaks can be added to a post with a special [new line character](/docs/apis/post/overview#line-breaks). Rich text, such as bold or italic lettering, can be added to a Threads post with a few [html elements](/docs/apis/post/overview#rich-text-posts). ## Carousel of Images and Videos You can post multiple images or videos to Threads as a carousel; up to a combined total of 20 images or videos may be use in a carousel. Just add your additional images or videos to the `mediaUrls` array and the carousel will automatically be created. ```json theme={"system"} "mediaUrls": ["https://url.com/image.jpg", "https://url.com/video.mp4" ...]; // Max 20 images or videos ``` Video URLs must end in a known extension such as mp4. ## Character Limits Please see [Threads Character Limits](/docs/help-center/technical-support/character-limits#threads-character-limits) for more information. ## Geo Restrictions You can restrict a post to a specific country or countries by using the `allowCountries` array. ```json theme={"system"} { "threadsOptions": { "allowCountries": ["US", "CA"] } } ```
    • `allowCountries`: An array of country codes to allow. See [country codes](/docs/iso-codes/country).
    > [!WARNING] > > Geo restrictions (geo-gating) on Threads are only available if Meta has enabled this feature for your account. > > Meta decides which Threads accounts are eligible for geo restrictions based on factors such as account verification, follower count, or content creator status. > If your account is eligible, you will see the geographic settings (globe icon) in the post composer when creating a new Threads post. > > You can check if a user profile is eligible for Threads geo restrictions by checking the `isEligibleForGeoRestrictions` property in the [/user](/docs/apis/user/profile-details) endpoint. ### Geo Restrictions on a Thread `allowCountries` now applies to a [thread](/docs/apis/post/social-networks/threads#thread) as well as to a single post. If the account is **not** eligible for geo restrictions, sending `allowCountries` with `thread: true` returns `code: 101` and **no part of the thread is published** — the same result you already get for a single post with that option. > [!WARNING] > > This is a change in behavior. Previously a thread with `allowCountries` published normally on > an ineligible account and the country restriction was silently dropped, so posts you intended > to restrict went out unrestricted with no indication. If you send `allowCountries` on threads > today from an ineligible account, those threads will now fail instead of publishing. > > Check `isEligibleForGeoRestrictions` on [/user](/docs/apis/user/profile-details) before sending > `allowCountries`, or remove the option to publish the thread without country restrictions. ## Threads Mentions Mention another Threads handle by adding @handle in the post text. For example: ```json theme={"system"} { "post": "The best social media API @Ayrshare ever!", "mediaUrls": ["https://images.com"], "platforms": ["threads"] } ``` > [!WARNING] > > The @mentioned user will be notified of the mention. Please review the [important > rules](/docs/testing/post-verification#mentions) on mentions. If the quota has been reached, an error message will be returned. ## Thread A Threads thread, also known as a threadstorm, is a connected series of posts on Threads that allows you to share longer ideas beyond a single post's character limit, appearing as one continuous narrative when viewed together. ### Posting a Thread A Threads thread can be posted via the API. A thread is a post broken up into a set of reply posts associated together in Threads. You can either automatically break up the post or specify the [thread breaks](/docs/apis/post/social-networks/threads#thread-breaks) in the post text. ```json Threads Thread theme={"system"} { "threadsOptions": { "thread": true, // required for threadstorm "threadNumber": true, // optional to add numbers to each thread "mediaUrls": ["https://site.com/image1.png", "https://site.com/image2.png", ...] // optional one media object is added to a thread in order } } ```
    • `thread: true` to automatically break apart the post text into threads based on line breaks.
    • `threadNumber: true` to automatically add numbers at the end of threads in the format of 1/n. For example, the 2nd of 5 threads will have appended: 2/5
    • `mediaUrls: [array of urls]` to add each media object, an image or video, to a thread in order. Only one media object will be added to a thread in order.
    #### Thread Media ##### Skip Media Skip media for a thread by using `null` in the array. For example: `["https://site.com/image1.png", null, "https://site.com/image2.png"]` This will place image1 on the first post, no image on the second post, and image2 on the third post. ##### Multiple Media Multiple media objects can be added to a single post within a thread by adding an object `{}` with the media URLs in the `mediaUrls` array. Any unique object keys can be used. For example: ```json Threads Thread with Multiple Media URLs theme={"system"} { "threadsOptions": { "thread": true, "threadNumber": true, "mediaUrls": [ "https://img.ayrshare.com/random/photo-1.jpg", { "1": "https://img.ayrshare.com/random/photo-2.jpg", "2": "https://img.ayrshare.com/random/photo-3.jpg" }, "https://img.ayrshare.com/random/photo-4.jpg" ] } } ``` In this example, the first post will contain photo-1.jpg, the second post photo-2.jpg and photo-3.jpg, and the third post photo-4.jpg. #### Thread Breaks Ayrshare automatically breaks up the post text into appropriate length posts (> 500 characters) for Threads. When creating threads, we prioritize keeping complete sentences in one post when possible. If a sentence won't fit, we split between sentences. For very long sentences, we split between words. In rare cases where a word is too long, we split the word itself. You may also manually add paragraphs with `\n\n` to the post text to indicate a unique thread should be created. If you have `\n\n` in the post text, we will not automatically break the post into threads. For example: ```json Example Threads Thread theme={"system"} { "post": "This is post 1\n\nThis is post 2.", "platforms": ["threads"], "threadsOptions": { "thread": true } } ``` will result in two posts in the thread. If you want to add paragraphs, but not break into posts, use `\u2063\n\u2063\n`. ```json Threads Thread with Paragraphs theme={"system"} { "post": "This is paragraph 1\u2063\n\u2063\nThis is paragraph 2.", "platforms": ["threads"], "threadsOptions": { "thread": true } } ``` will result in one post with two paragraphs since the post is below 500 characters. ### Delete a Thread Delete a published Threads post with the [delete endpoint](/docs/apis/post/delete-post), using the Ayrshare post `id`. # TikTok API Source: https://www.ayrshare.com/docs/apis/post/social-networks/tiktok Publish TikTok video and image posts, handle TikTok processing, and set visibility options, video thumbnails, mentions, and drafts with the TikTok API. > [!NOTE] > > Ayrshare offers [direct publishing of TikTok videos](https://www.ayrshare.com/blog/introducing-tiktok-direct-publishing-analytics-and-commenting/), comment management, and retrieval of advanced analytics for either your personal or business TikTok account using the TikTok API. > > TikTok asynchronously processes video and photos, so the JSON response will be status: "pending" for both immediate and scheduled posts. Once TikTok has completed processing, your registered [Scheduled Action webhook](/docs/apis/webhooks/actions#scheduled-action) will be called. > [!NOTE] > > When Ayrshare converts a WebP, HEIC, HEIF, or AVIF image to JPEG for a photo post it preserves the image's color rendering and writes a small XMP packet carrying only the `Iptc4xmpExt:DigitalSourceType` AI disclosure, when the image declares one. No other metadata is carried over: not EXIF, and nothing else from the original XMP, so a location, a creator name or a copyright line can't be published by accident. The C2PA cryptographic signature > does not survive a re-encode. This is independent of the video-only `isAIGenerated` > toggle documented below. See [Image Metadata and Content > Credentials](/docs/media-guidelines/overview#image-metadata-and-content-credentials). ## TikTok Video Post JSON for a basic TikTok video post that is directly published: ```json TikTok Video Post theme={"system"} { "post": "The best TikTok \n video ever #bestvideo", // Max 2,200 characters with a line break "mediaUrls": ["https://img.ayrshare.com/012/tiktok.mp4"], "platforms": ["tiktok"] } ``` Example JSON post response: ```json TikTok Video Post Response theme={"system"} { "status": "success", "errors": [], "postIds": [ { "status": "success", "idShare": "video.7088122496758679353.nzLqBWbf", "id": "pending", "isVideo": true, "platform": "tiktok" } ], "id": "lb42orDhySAZmLWtj6b6", "refId": "23a9da9e0df1184a7a6a1fc2c60b8023aa9a32a1", "post": "The best TikTok video ever #bestvideo" } ```
    • TikTok does not currently support line breaks in the post text. Included line breaks will be ignored.
    • Either one video or up to 35 images may be published. TikTok does not support a combination of video and images. Please see below for more details.
    • If the video does not end in a known extension, use [isVideo](/docs/apis/post/overview#video-extension).
    • TikTok also supports sending media without post text. If you do not want post text included send an empty String `post: ""`.
    • See [TikTok Media Guidelines](/docs/media-guidelines/tiktok) and [TikTok Authorization](/docs/dashboard/connect-social-accounts/tiktok) for more information.
    ### TikTok Video Requirements
    • Please see [TikTok Video Requirements](/docs/media-guidelines/tiktok#video).
    • The video must end in a known video extension such as mp4. Please either reverse proxy the URL, add on a [vanity URL with a CDN](https://www.ayrshare.com/blog/how-to-put-a-cdn-in-front-of-firebase-cloud-storage/), or use the [/media](/docs/apis/media/overview) endpoint.
    • TikTok's post text character limit is 2,200.
    > [!NOTE] > > TikTok limits the API video publishing to 6 videos per minute with an upper limit of 15 videos per > day. ## TikTok Image Post JSON for a basic TikTok image (photo) post that is directly published: ```json TikTok Image Post theme={"system"} { "post": "The best TikTok \n video ever #bestvideo", // Max 2,200 characters with a line break "mediaUrls": [ "https://img.ayrshare.com/012/gb.jpg", "https://img.ayrshare.com/random/photo-1.jpg" ], // Up to 35 images "platforms": ["tiktok"] } ``` Example JSON response: ```json TikTok Image Post Response theme={"system"} { "status": "success", "errors": [], "postIds": [ { "status": "success", "idShare": "p_pub_url~v2.7408974036430047275", "id": "pending", "isVideo": false, "platform": "tiktok" } ], "id": "8815mJ5bWApEebWjE233", "tikTokId": "p_pub_url~v2.7408974036430047333", "refId": "9abf1426d6ce9122ef11c72bd62e59807c5cc333", "post": "Opportunity is missed by most people because it is dressed in overalls and looks like work - Thomas Edison" } ```
    • TikTok does not currently support line breaks in the post text. Included line breaks will be ignored.
    • Either one video or up to 35 images may be published.
    • TikTok does not support a combination of video and images. Please see below for more details.
    • The images must be of type JPG, JPEG, or WEBP. TikTok does not accept PNG media files.
    • You may also select one of the images as the cover photo with the `imageCoverIndex`. By default, the first image is used. Please see below for details.
    ### TikTok Image Requirements
    • Please see [TikTok Image Requirements](/docs/media-guidelines/tiktok#images).
    • Up to 35 images may be included in a post, at 20 MB per image.
    • The images must be of type JPG, JPEG, or WEBP. TikTok does not accept PNG media files.
    • TikTok's post text character limit is 2,200.
    > [!NOTE] > > TikTok limits the API video publishing to 6 photos per minute with an upper limit of 15 photos per > day. ## TikTok Processing TikTok does asynchronous processing of videos and images, so the response will have the `id` field set to `"pending"`. After TikTok completes their processing, usually within 1 - 2 minutes, the `id` field will be updated with the TikTok video `id` and a `postUrl` will be added.
    • You can retrieve the final status of the TikTok post using [webhooks](/docs/apis/webhooks/actions#tiktok-publishing-webhook) or the [/history](/docs/apis/history/overview) endpoint and usually takes up to 1-2 minutes to be available.
    • When the user publishes the video in the TikTok mobile app, a "scheduled" webhook will be sent with the `subAction: "tikTokPublished"`.
    • A [first comment](/docs/apis/post/overview#first-comment) on a TikTok post is deferred: it is posted automatically once the `tikTokPublished` webhook resolves the real video `id`, not at publish time. The video `visibility` must be `public`, otherwise the first comment cannot be posted and a comment error is returned.
    • If an error occurs, such as TikTok was unable to process the video or Ayrshare internal tests failed, the `id` field will be set to "failed" and the `errors` field will contain the error details.
    • The `idShare` is used for internal referencing the pending video.
    ## TikTok Options When publishing TikTok videos or images, [additional options](/docs/apis/post/social-networks/tiktok#options) are available. Video Publishing Example: ```json TikTok Video Publishing theme={"system"} { "tikTokOptions": { "disableComments": true, // Default false. Disable comments on the published video. "disableDuet": true, // Default false. Disable duets on the published video. "disableStitch": true // Default false. Disable stitches on the published video. } } ``` Image Publishing Example: ```json TikTok Image Publishing theme={"system"} { "tikTokOptions": { "imageCoverIndex": 1, // Use the second image in the mediaUrls. "title": "Amazing images" } } ``` ### Options The following options are available for TikTok posts. They should be added to the `tikTokOptions` object. Please see below for more details on each option. ```json TikTok Options theme={"system"} { "post": "The best TikTok video ever #bestvideo", "mediaUrls": ["https://img.ayrshare.com/012/tiktok.mp4"], "platforms": ["tiktok"], "tikTokOptions": { "autoAddMusic": true, "disableComments": true, "disableDuet": true, "disableStitch": true, "draft": true, "isAIGenerated": true, "isBrandedContent": true, "isBrandOrganic": true, "imageCoverIndex": 1, "title": "Amazing images", "thumbNailOffset": 30000, "visibility": "public" } } ``` **autoAddMusic** `boolean` (default: false) Whether to automatically add recommended music to the post. If you set this field to `true`, you can change the music later in the TikTok app. Media type: image **disableComments** `boolean` (default: false) Whether to disable comments on the published post. Media type: video, image **disableDuet** `boolean` (default: false) Disable duets on the published video. Media type: video **disableStitch** `boolean` (default: false) Disable stitch on the published video. Media type: video **draft** `boolean` (default: false) Whether to create a draft post. See [draft options](/docs/apis/post/social-networks/tiktok#tiktok-draft-post) for more information. Media type: video or image **isAIGenerated** `boolean` (default: false) Whether to enable the AI-generated content toggle for the video post. If you enable the toggle, your video will be labeled as "Creator labeled as AI-generated" once posted and can't be changed. The "Creator labeled as AI-generated" label indicates that the content was completely AI-generated or significantly edited with AI. > [!NOTE] > > Turning on the AI-generated content setting won't affect the distribution of your video as long as > it doesn't violate TikTok's [Community > Guidelines](https://www.tiktok.com/community-guidelines/en/). Media type: video **isBrandedContent** `boolean` (default: false) Whether to enable the [Branded Content](https://creatormarketplace.tiktok.com/help#/doc/9493/10008169) toggle. If this field is set to `true`, the video will be labeled as Branded Content, indicating you are in a paid partnership with a brand. A "Paid partnership" label will be attached to the video. Media type: video, image **isBrandOrganic** `boolean` (default: false) Whether to enable the Brand Organic Content toggle. If this field is set to `true`, the video will be labeled as Brand Organic Content, indicating you are promoting yourself or your own business. A "Promotional content" label will be attached to the video. Media type: video, image **imageCoverIndex** `number` (default: 0) The index of the `mediaUrls` to be used as the cover for the post. Media type: image **title** `string` The title of the post. Media type: image **thumbNailOffset** `number` The frame to use for the video cover. See [video thumbnail options](/docs/apis/post/social-networks/tiktok#video-thumbnail) for more information. Media type: video **visibility** `string` (default: public) How the post is shared and who can see it. Values: `public`, `private`, `followers`, or `friends`. See [visibility options](/docs/apis/post/social-networks/tiktok#visibility-options) for more information. Media type: video, image ### Visibility Options | Visibility | Description | | :- | :- | | public | Visible to all TikTok users. | | private | Private, only visible to the account itself. | | followers | Only visible to followers of the account. Requires a private TikTok account. | | friends | Only visible to mutual followers. | > [!WARNING] > > TikTok only offers the `followers` option on private TikTok accounts. If the > account is public, TikTok accepts the post without an error and publishes it > visible to everyone. TikTok Business accounts are always public, so they can't > use `followers`. Switching an account from private to public also changes > existing followers-only posts to be visible to everyone. Private posts will remain in a status `pending` and no TikTok webhook will be sent until the post is made public. ## Video Thumbnail There are two ways to set a thumbnail, also known as a cover photo, for a TikTok video: 1. Using the `thumbNailOffset` parameter to set a thumbnail frame. 2. Using the `thumbNail` parameter to set a thumbnail image from a URL. Only videos are supported for setting a thumbnail. ### Thumbnail Offset Set a thumbnail for a TikTok video by selecting an offset frame. ```json TikTok Video Thumbnail Offset theme={"system"} { "tikTokOptions": { "thumbNailOffset": 30000 // milliseconds of offset image } } ``` The offset is the location in milliseconds of the thumbnail frame. Default value is `0`, which is the first frame of the video. ### Thumbnail URL Set a thumbnail for a TikTok video by uploading an image from a URL. ```json TikTok Video Thumbnail URL theme={"system"} { "tikTokOptions": { "thumbNail": "https://img.ayrshare.com/012/gb.jpg" } } ``` If you use the `thumbNail` parameter, the `thumbNailOffset` parameter will be ignored. Please see [TikTok Thumbnail Requirements](/docs/media-guidelines/tiktok#video-thumbnail) for more information. ## TikTok Mentions Mention another TikTok handle by adding `@handle` in the post text. For example: ```json TikTok Mention theme={"system"} { "post": "Love the @ayrshare social media api" } ``` > [!WARNING] > > Please review the [important rules](/docs/testing/post-verification#mentions) on mentions. ## TikTok Draft Post Create a draft post for a TikTok video or image, allowing you to edit the video or image before publishing. ```json TikTok Video Draft Post theme={"system"} { "post": "The best TikTok video ever #bestvideo", // empty string is allowed "mediaUrls": ["https://img.ayrshare.com/012/tiktok.mp4"], "platforms": ["tiktok"], "tikTokOptions": { "draft": true } } ``` The draft video or image post will be found under the notifications **Inbox** in bottom row of the TikTok app. Look for a **System notifications** message and then click the top **Your content from Ayrshare is ready** message. The Ayrshare `postUrl` will remain in `pending` status until the video is published. First comment not supported on draft posts. ![TikTok Draft Post](https://mintcdn.com/ayrshare-docs/Nmrhj2Gh7WSf62Bh/images/apis/post/tiktok-draft-post.webp?fit=max\&auto=format\&n=Nmrhj2Gh7WSf62Bh\&q=85\&s=6e7fdce5ef106f6af6cd060bfbc446ba) ## Authorization Refresh TikTok must be reauthorized *every year* via the Social Accounts page.
    • An email and webhook social action notification will be sent 15 days in advance of authorization expiration.
    • The refresh required date and remaining days can be retrieved from the [/user](/docs/apis/user/overview) endpoint.
    ## Character Limits Please see [TikTok Character Limits](/docs/help-center/technical-support/character-limits#tiktok-character-limits) for more information. ## Additional Information Additional [examples on using the TikTok API](https://www.ayrshare.com/blog/tiktok-api-how-to-post-to-tiktok-using-a-social-media-api/). # X API Source: https://www.ayrshare.com/docs/apis/post/social-networks/x-twitter Post to X with your own API keys, using polls, quote tweets, reply settings, long posts and videos, alt text, geo restrictions, subtitles, and threads. ## X BYO API Keys > [!NOTE] > > Starting March 31, 2026, all X/Twitter operations require your own API credentials. After linking your X account via OAuth, include these 2 headers in every request that targets X: > > | Header | Value | > | - | - | > | `X-Twitter-OAuth1-Api-Key` | Your API Key (Consumer Key) | > | `X-Twitter-OAuth1-Api-Secret` | Your API Key Secret (Consumer Secret) | > > Not linked yet? See the [X BYO Key Setup Guide](/docs/dashboard/connect-social-accounts/x-twitter-byo-keys) to connect your X account. > [!NOTE] > > Ayrshare no longer imposes its own monthly or daily X/Twitter rate limits. Because X requests use > your own credentials (BYO), your usage is governed solely by your own X Developer App limits. See > the [X API rate limits > documentation](https://developer.x.com/en/docs/x-api/rate-limits) for details. ## Posting to X (Twitter) JSON for a basic post with a link and image using the X API, formerly known as the Twitter API: ```json X Post theme={"system"} { "post": "The best Tweet ever #best https://www.twitter.com", // empty string is allowed "mediaUrls": ["https://img.ayrshare.com/012/gb.jpg"], "platforms": ["twitter"] } ```
    • X will automatically preview the link in the Tweet unless there is an image or video included. In the above example the image will show. Removing the image will cause the link preview to show.
    • If your video doesn't end in a known video extension such as mp4, please use the `isVideo` parameter. See the [/post endpoint](/docs/apis/post/post) for details.
    • X also supports sending media without post text. If you do not want post text included send an empty String `post: ""`.
    • You may upload up to 4 images or videos in a single Tweet. Please see the important guidelines and restrictions [posting Twitter videos](/docs/media-guidelines/x-twitter).
    • See [X Media Guidelines](/docs/media-guidelines/x-twitter) and [X Authorization](/docs/dashboard/connect-social-accounts/x-twitter) for more information.
    ## X Options You can set additional options for a post by using the `twitterOptions` parameter. ```json X Options theme={"system"} { "twitterOptions": { "altText": ["This is my best pic", "😃 here is the next one"], "blockCountries": ["US", "CA"], // or "allowCountries": ["GB", "IE"] "longPost": true, "longVideo": true, "poll": { "duration": 5, // required. Number in minutes "options": ["yes", "maybe", "no"] // required }, "quoteTweetId": "651601430669664256", "replySettings": "mentioned", "subscribersOnly": true, "isAIGenerated": true, "thumbNail": "https://img.ayrshare.com/012/gb.jpg", // Video Metadata "videoTitle": "My Product Demo", "videoDescription": "A short walkthrough of our latest release.", "embeddable": false, // single-video posts only // Subtitles / Captions for Videos "subTitleUrl": "https://img.ayrshare.com/012/captions.srt", "subTitleLanguage": "en", "subTitleName": "English", // Thread Options "thread": true, "threadNumber": true, "mediaUrls": ["https://img.ayrshare.com/012/gb.jpg"], } } ``` X options are optional fields that can be used to control the post. > [!NOTE] > > The block above lists the available options together; it is not a set that should all be sent > at once. When `thread` is set, see [which options apply to a > thread](/docs/apis/post/social-networks/x-twitter#thread-option-scoping) — most apply to every post > in the thread, a few only to the first, and `longPost` has no effect at all. **altText** `array of strings` Alternative text for images to help with accessibility and screen readers. Maximum 1,000 characters per alt text. See [Alt Text](/docs/apis/post/social-networks/x-twitter#alt-text) for more information. **blockCountries** `array of strings` Restrict media to specific countries by blocking certain regions. Use [country codes](/docs/iso-codes/country). Cannot be used with `allowCountries`. See [Geo Restrictions](/docs/apis/post/social-networks/x-twitter#geo-restrictions) for more information. **allowCountries** `array of strings` Restrict media to specific countries by allowing certain regions. Use [country codes](/docs/iso-codes/country). Cannot be used with `blockCountries`. See [Geo Restrictions](/docs/apis/post/social-networks/x-twitter#geo-restrictions) for more information. **longPost** `boolean` (default: false) Enable posting of longer posts up to 25,000 characters for Premium users. See [Long Post](/docs/apis/post/social-networks/x-twitter#long-post) for more information. **longVideo** `boolean` (default: false) Allow posting videos longer than 2 minutes 20 seconds for approved accounts. See [Long Video](/docs/apis/post/social-networks/x-twitter#long-video) for more information. **poll** `object` Conduct polls with custom options and duration. Required fields: `duration` (number in minutes), `options` (array of strings). See [Polls](/docs/apis/post/social-networks/x-twitter#polls) for more information. **quoteTweetId** `string` Quote another tweet by specifying the Tweet ID. See [Quote Tweet](/docs/apis/post/social-networks/x-twitter#quote-tweet) for more information. **replySettings** `string` Control who can reply to the post. Values: `following`, `mentioned`, `subscribers`, or `verified`. See [Reply Settings](/docs/apis/post/social-networks/x-twitter#reply-settings) for more information. **subscribersOnly** `boolean` (default: false) Make the post visible only to subscribers. See [Subscribers Only](/docs/apis/post/social-networks/x-twitter#subscribers-only) for more information. **isAIGenerated** `boolean` (default: false) Apply X's "Made with AI" label to disclose that the post contains AI-generated media. Both `true` and `"true"` apply the label. Any other value leaves the label off — the post still publishes and the response adds a `warnings` entry explaining why. > [!NOTE] > > The label applies only to posts that include media, and cannot be changed after posting. Applies to > immediate, scheduled, and threaded posts. Media type: image, video See [Made with AI](/docs/apis/post/social-networks/x-twitter#made-with-ai) for more information. **subTitleUrl** `string` Add subtitles/captions to videos using SRT files. Must be a valid SRT file URL ending in `.srt`. See [Subtitles / Captions for Videos](/docs/apis/post/social-networks/x-twitter#subtitles-/-captions-for-videos) for more information. **subTitleLanguage** `string` (default: en) The language of the subtitles. Must be a valid [language code](/docs/iso-codes/language). **subTitleName** `string` The name of the caption track. Maximum 150 characters. **thumbNail** `string` Set a thumbnail (cover image) for videos. Must be a URL to a JPEG, PNG, BMP, or WebP image file. See [Video Thumbnail](/docs/apis/post/social-networks/x-twitter#video-thumbnail) for more information and [X Media Guidelines](/docs/media-guidelines/x-twitter#video-thumbnail) for image requirements. **videoTitle** `string` Set the title for a video. Maps to the title field in X Media Studio. See [Video Metadata](/docs/apis/post/social-networks/x-twitter#video-metadata) for more information. **videoDescription** `string` Set the description for a video. Maps to the description field in X Media Studio. See [Video Metadata](/docs/apis/post/social-networks/x-twitter#video-metadata) for more information. **embeddable** `boolean` (default: true) Control whether the video plays inside embedded posts on other websites. X's default is `true`. Set `false` to stop the video playing in embeds. Pressing play on an embed sends the viewer to x.com, and the video still plays on x.com as normal. Applies only to a post with a single video. On any other post, the post still publishes and the response adds the warning `embeddable applies to a post with a single video; it was not applied.` A value that is not a boolean is ignored. Media type: video **thread** `boolean` (default: false) Break long posts into connected thread series with optional numbering and media. See [Threads](/docs/apis/post/social-networks/x-twitter#thread) for more information. **threadNumber** `boolean` (default: false) Automatically add numbers at the end of threads in the format of 1/n. Requires `thread: true`. **mediaUrls** `array of strings` Add media objects to threads. One media object will be added to each thread in order. Use `null` to skip media for a specific thread. Use objects with multiple URLs for multiple media per thread. ## Alt Text Add alternative text, also known as alt text, to a Tweet's image. X alt text is an accessibility feature used for additional user info and screen readers. Use the `altText` in the `twitterOptions` object. ```json X Alt Text theme={"system"} { "twitterOptions": { "altText": ["This is my best pic", "😃 here is the next one"] // Array of Alt Texts } } ``` Each alt text must correspond to an image in the `mediaUrls` array. The alt text will be applied to each image in order. > [!NOTE] > > Alt text cannot be applied to videos. If a video is included in the `mediaUrls` with `altText`, > the video will not be posted. Alt text must be 1,000 characters or less. ## Geo Restrictions You can restrict X media, such as an image or video, to a specific countries by using the `blockCountries` and `allowCountries` parameters with [country codes](/docs/iso-codes/country). The post will still show in all countries, but the media will not be available in the countries specified. ```json Block Countries theme={"system"} { "twitterOptions": { "blockCountries": ["US", "CA"] } } ``` ```json Allow Countries theme={"system"} { "twitterOptions": { "allowCountries": ["GB", "IE"] } } ```
    • `blockCountries`: An array of country codes to block. See [country codes](/docs/iso-codes/country).
    • `allowCountries`: An array of country codes to allow. See [country codes](/docs/iso-codes/country).
    You must only use one of the parameters `blockCountries` or `allowCountries` at a time. If both parameters are used or a country is not supported by X, the geo restrictions will be ignored. ## Long Post Users with Premium X accounts, such as Premium or Premium Plus, have the ability to post longer posts of up to 25,000 characters. Ayrshare automatically allows long from posts for users with Premium X accounts. If the user changes their X Premium status, please wait 24 hours for it to reflect in Ayrshare. You can check the Premium subscription status of a user with the [/user](/docs/apis/user/overview) or [/analytics](/docs/apis/analytics/social) endpoints. You may also force a long form post to be accepted with the `longPost` body parameter. This can be done by including the following JSON in your request: ```json X Long Post theme={"system"} { "twitterOptions": { "longPost": true } } ``` However, if a user without a Premium account attempts to post a long tweet, the system will return a `code: 111` error. > [!NOTE] > > `longPost` has no effect when `thread` is also set — a [thread](/docs/apis/post/social-networks/x-twitter#thread) > splits your text across separate posts, so nothing is published long form. The thread > publishes normally and the response includes a warning saying `longPost` was ignored. Remove > `thread` to publish a single long post. ## Long Video *Business or Enterprise Plan required.* X requires videos have a [maximum video length](/docs/media-guidelines/x-twitter#video) of 2 minutes and 20 seconds. However, if X has approved the account to upload longer videos — for example an X Premium account, or one in the [Amplify Partner Program](https://media.twitter.com/en/articles/products/2018/in-stream-video-ads-for-publishers) — you can post videos up to **4 hours** in length. > [!WARNING] > > Please verify that your user's X account is Premium or in the [Amplify Partner > Program](https://media.twitter.com/en/articles/products/2018/in-stream-video-ads-for-publishers) > before using the `longVideo` parameter. If your user's X account is not authorized to post long > videos, the system will return an error. Use the `longVideo` twitterOptions parameter when posting a long video: ```json X Long Video theme={"system"} { "twitterOptions": { "longVideo": true } } ``` ## Mentions Mention another X handle by adding `@handle` in the post text. For example: ```json X Mention theme={"system"} { "post": "The best social media API @Ayrshare ever!", "platforms": ["twitter"] } ``` > [!WARNING] > > Please review the [important rules](/docs/testing/post-verification#mentions) on mentions. ## Polls Conduct an X Poll with the `twitterOptions` `poll` parameter. ```json X Poll theme={"system"} { "twitterOptions": { "poll": { "duration": 5, // required. Number in minutes "options": ["yes", "maybe", "no"] // required } } } ```
    • `duration`: A number of minutes specifying the duration the poll will be conducted.
    • `options`: An array of strings of the poll options.
    ## Quote Tweet You can quote another Tweet by specifying the low-level Tweet ID. The ID can be retrieved via the [`/post`](/docs/apis/post/post) response in the `postIds` field, [get history](/docs/apis/history/get-history), or directly from the Tweet URL: [https://twitter.com/Ayrshare/status/**1651601430669664256**](https://twitter.com/Ayrshare/status/1651601430669664256) ```json X Quote Tweet theme={"system"} { "twitterOptions": { "quoteTweetId": "651601430669664256" // low-level Tweet Id } } ``` ## Reply Settings You can set the reply settings for a post to only allow replies by certian types of users. ```json X Reply Settings theme={"system"} { "twitterOptions": { "replySettings": "mentioned" } } ``` The `replySettings` parameter can be one of the following values:
    • `following`: Only users who the X account is following can reply.
    • `mentioned`: Only users who are mentioned in the post can reply.
    • `subscribers`: Only users who are subscribers to the X account who posted the post can reply.
    • `verified`: Only users who are verified on X can reply to the post.
    ## Subscribers Only You can set a post to only be visible to subscribers by using the `subscribersOnly` parameter. ```json X Subscribers Only theme={"system"} { "twitterOptions": { "subscribersOnly": true } } ``` ## Made with AI Apply X's native "Made with AI" label to disclose that a post contains AI-generated media. Set the `isAIGenerated` field in the `twitterOptions` object to `true`. ```json X Made with AI theme={"system"} { "twitterOptions": { "isAIGenerated": true } } ``` ### Accepted Values Both the boolean `true` and the string `"true"` apply the label; `false` and `"false"` leave it off. Any other value — `"yes"` or `1`, for example — also leaves the label off, but never fails the request: the post publishes normally and the response adds a `warnings` entry listing the accepted values. > [!NOTE] > > The Ayrshare [MCP Server](/docs/additional/mcp-action-server) takes `isAIGenerated` as a strict boolean — > pass `true`, not `"true"`. > [!NOTE] > > The label applies only to posts that include media (image or video). It has no effect on text-only > posts, and it cannot be changed after the post is published. > [!NOTE] > > Applies to immediate, scheduled, and threaded posts — a scheduled post publishes with the same "Made > with AI" label it would have received if posted immediately. On a thread, each part that includes > media is labeled. ## Subtitles / Captions for Videos You can add X subtitles, also known as X captions, to videos by including an [SRT file](https://en.wikipedia.org/wiki/SubRip). Use the `subTitleUrl` field in the `twitterOptions` object to specify the URL to your SRT file. ```json X Subtitles theme={"system"} { "twitterOptions": { "subTitleUrl": "https://img.ayrshare.com/012/captions.srt", "subTitleLanguage": "en", "subTitleName": "English" } } ```
    • `subTitleUrl`: A valid SRT file. The URL must start with `https://` and end in `.srt` and be a valid SRT file.
    • `subTitleLanguage`: Optional: The language of the subtitles. Must be a valid [language code](/docs/iso-codes/language). Default: "en".
    • `subTitleName`: Optional: The name of the caption track. The name is intended to be visible to the user as an option during playback. The maximum name length supported is 150 characters. Default: "English".
    ## Video Thumbnail Set a thumbnail (cover image) for Twitter/X videos. The thumbnail is displayed before the video plays and helps users understand the video content. Use the `thumbNail` in the `twitterOptions` object. ```json Twitter/X Video Thumbnail theme={"system"} { "twitterOptions": { "thumbNail": "https://img.ayrshare.com/012/gb.jpg" } } ```
    • "thumbNail": A URL to the thumbnail image. Supported image formats are JPEG, PNG, BMP, and WebP.
    • The thumbnail image should represent the video content and be visually appealing to encourage engagement.
    • See [X Media Guidelines](/docs/media-guidelines/x-twitter#video-thumbnail) for image requirements.
    ## Video Metadata Set a title and description for a video posted to X. These map to the title and description fields in X Media Studio. Use the `videoTitle` and `videoDescription` fields in the `twitterOptions` object. ```json X Video Metadata theme={"system"} { "twitterOptions": { "videoTitle": "My Product Demo", "videoDescription": "A short walkthrough of our latest release." } } ```
    • `videoTitle`: The title for the video, mapped to the X Media Studio title field.
    • `videoDescription`: The description for the video, mapped to the X Media Studio description field.
    ## Video Monetization (Pro Media) Ayrshare supports X (Twitter) video monetization for eligible accounts through X's Pro Media program. This is a limited-access capability — [contact us](mailto:support@ayrshare.com) if you'd like access. ## Thread An X Thread, also known as a tweetstorm, is a connected series of posts on X (formerly Twitter) that allows you to share longer ideas beyond a single post's character limit, appearing as one continuous narrative when viewed together. ![X Thread](https://mintcdn.com/ayrshare-docs/Nmrhj2Gh7WSf62Bh/images/apis/post/x-thread.webp?fit=max\&auto=format\&n=Nmrhj2Gh7WSf62Bh\&q=85\&s=35bd3fbb8c2c1af6a4b3d96b8dae54d8) ### Posting a Thread A X Thread can be posted via the API. A thread is a post broken up into a set of reply threads and associated in X with a line. You can either automatically break up the post or specify the [thread breaks](/docs/apis/post/social-networks/x-twitter#thread-breaks) in the post text. ```json X Thread theme={"system"} { "twitterOptions": { "thread": true, // required for TweetStorm "threadNumber": true, // optional to add numbers to each thread "mediaUrls": ["https://site.com/image1.png", "https://site.com/image2.png", ...] // optional one media object is added to a thread in order } } ```
    • `thread: true` to automatically break apart the post text into threads based on line breaks.
    • `threadNumber: true` to automatically add numbers at the end of threads in the format of 1/n. For example the 2nd of 5 threads will have appended: 2/5
    • `mediaUrls: [array of urls]` to add each media object, an image or video, to a thread in order. Only one media object will be added to a thread in order.
    If the post is sent as a X Thread, the returned post analytics will be an array of Tweets, `"twitter": []`. See [Post Analytics 200 Response](/docs/apis/analytics/post) for more information. #### Which twitterOptions Apply to a Thread The other `twitterOptions` you send apply to a thread as well, not only to a single post. Most apply to **every** post in the thread; a few apply only to the **first** post, because that is where X accepts them.
    • **First post only** — `poll`, `quoteTweetId`, `replyId` and `replySettings`. A poll or a quote belongs to the post that opens the thread, and X only accepts reply settings on the post that starts a conversation, so sending them on a reply is rejected. Setting `replySettings` on a thread gates replies for the whole conversation from that first post.
    • **`replySettings` cannot be combined with `replyId`.** X does not accept reply settings on a post that is itself a reply, so a thread posted as a reply to an existing post cannot also set `replySettings` — the request is rejected and nothing publishes. This is the same restriction a single reply has always had. Send one or the other.
    • **Every post** — everything else, including `subscribersOnly`, `longVideo` and the Pro Media options. `subscribersOnly` applies to every post so that later posts in an exclusive thread are not public.
    • **Media-specific** — `altText` and subtitles apply to the media on the **first** post. Media on later posts is published without them.
    > [!WARNING] > > An option your account is not entitled to use now fails the whole thread rather than being > ignored. For example, sending `subscribersOnly: true` from an account without X Subscriptions > returns `code: 111` and nothing is published — previously the thread published and the option > was silently dropped. Check your account's entitlements, or remove the option. `longVideo` works on a thread: attach the video with `mediaUrls` and it is verified against the long-video limit rather than the standard 2 minutes and 20 seconds. See [Long Video](/docs/apis/post/social-networks/x-twitter#long-video). `longPost` and `thread` cannot both take effect — a thread splits your text across separate posts, so nothing is published long form. The thread still publishes and the response carries a warning saying `longPost` was ignored. Remove `thread` to publish a single long post. #### Thread Media ##### Skip Media Skip media for the thread by using `null` in the array. For example: `["https://site.com/image1.png", null, "https://site.com/image2.png"]` This will place image1 on the first Tweet, no image on the second Tweet, and image2 on the third Tweet. ##### Multiple Media Multiple media objects can be added to a Tweet in a Thread by adding an object `{}` with the media URLs in the `mediaUrls` array. Any unique object keys can be used. For example: ```json X Thread with Multiple Media URLs theme={"system"} { "twitterOptions": { "thread": true, "threadNumber": true, "mediaUrls": [ "https://img.ayrshare.com/random/photo-1.jpg", { "1": "https://img.ayrshare.com/random/photo-2.jpg", "2": "https://img.ayrshare.com/random/photo-3.jpg" }, "https://img.ayrshare.com/random/photo-4.jpg" ] } } ``` In this example, the first Tweet will contain photo-1.jpg, the second Tweet photo-2.jpg and photo-3.jpg, and the third Tweet photo-4.jpg. ##### Allowed mediaUrls Items Each item in a thread's `mediaUrls` must be one of:
    • A URL string — one media object for that Tweet.
    • `null` — no media for that Tweet.
    • An object `{}` of URL strings — several media objects for that Tweet.
    An item can never be an array. Use an object, not an array, to add several media objects to one Tweet. ```json Wrong - Array Items theme={"system"} "mediaUrls": [["https://img.ayrshare.com/random/photo-1.jpg"], ["https://img.ayrshare.com/random/photo-2.jpg"]] ``` ```json Right - Object Items theme={"system"} "mediaUrls": [{ "a": "https://img.ayrshare.com/random/photo-1.jpg" }, { "b": "https://img.ayrshare.com/random/photo-2.jpg" }] ``` Any other item, such as an array, a number, or an object with a number or array value, returns [`code: 527`](/docs/errors/errors-ayrshare#thread-and-media-option-errors) with HTTP `400`. The `details` field names the first item to fix, for example `twitterOptions.mediaUrls[0]`. #### Thread Breaks Ayrshare automatically breaks up the post text into appropriate length tweet (> 280 characters). When creating threads, we prioritize keeping complete sentences in one post when possible. If a sentence won't fit, we split between sentences. For very long sentences, we split between words. In rare cases where a word is too long, we split the word itself. You may also manually add paragraphs with `\n\n` to the post text to indicate a unique thread should be created. If you have `\n\n` in the post text, we will not automatically break the post into threads. For example: ```json Example X Thread theme={"system"} { "post": "This is tweet 1\n\nThis is tweet 2.", "platforms": ["twitter"], "twitterOptions": { "thread": true } } ``` will result in two Tweets in the thread. If you want to add paragraphs, but not break into Tweets, use `\u2063\n\u2063\n` ```json X Thread with Paragraphs theme={"system"} { "post": "This is paragraph 1\u2063\n\u2063\nThis is paragraph 2.", "platforms": ["twitter"], "twitterOptions": { "thread": true } } ``` will result in one Tweet with two paragraphs since the post is below 280 characters. ### Delete a Thread To delete a Tweet Storm, call the [/post delete endpoint](/docs/apis/post/delete-post) with the top level post ID returned in the response. All threads will be deleted. ## Character Limits Please see [X/Twitter Character Limits](/docs/help-center/technical-support/character-limits#x/twitter-character-limits) for more information. ## X Video Compatibility Some video software creates MP4 files that are not compatible with X. For example, Camtasia versions older than 2019.0.9 create MP4 files that X rejects. Also more than one audio track often causes problems. If you receive back the following message while posting, it indicates the video is not compatible with Twitter and needs to be re-encoded. `"file is currently unsupported"` Please check your video software for compatibility. For example, Adobe Media Encoder has an export preset for Twitter 1080p Full HD. ![Adobe Media Encoder](https://mintcdn.com/ayrshare-docs/Nmrhj2Gh7WSf62Bh/images/apis/post/tw-video.webp?fit=max\&auto=format\&n=Nmrhj2Gh7WSf62Bh\&q=85\&s=71016a72242be3d17af4bee05aee915a) Please see here for [more X API examples](https://www.ayrshare.com/twitter-api-how-to-post-and-get-analytics-with-the-twitter-api#twitter-api-examples). ## Subscribers Only You can set a post to only be visible to subscribers by using the `subscribersOnly` parameter. ```json X Subscribers Only theme={"system"} { "twitterOptions": { "subscribersOnly": true } } ``` ## Reply Settings You can set the reply settings for a post to only allow replies by certian types of users. ```json X Reply Settings theme={"system"} { "twitterOptions": { "replySettings": "mentioned" } } ``` The `replySettings` parameter can be one of the following values:
    • `following`: Only users who the X account is following can reply.
    • `mentioned`: Only users who are mentioned in the post can reply.
    • `subscribers`: Only users who are subscribers to the X account who posted the post can reply.
    • `verified`: Only users who are verified on X can reply to the post.
    # YouTube API Source: https://www.ayrshare.com/docs/apis/post/social-networks/youtube Upload YouTube videos and Shorts with titles, thumbnails, playlists, tags, subtitles, location targeting, and AI content disclosure, plus upload limits. > [!WARNING] > > YouTube posting requires your YouTube account to have at least one Channel and be an owner on the Channel. > To create a YouTube Channel, click on your profile in the YouTube Dashboard and choose "Create a Channel". > You may also use this direct link to create a YouTube Channel: [http://m.youtube.com/create\_channel](http://m.youtube.com/create_channel) > > If you're having issues viewing YouTube channels please see the [YouTube channel troubleshooting guide](/docs/help-center/technical-support/youtube-channels-not-showing). > [!NOTE] > > YouTube upload failures may return error codes **453** (timeout) or **454** > (service unavailable) with `retryAvailable: true`. Your integration can branch > on this flag to retry transient failures automatically with backoff. See the > [error codes reference](/docs/errors/errors-ayrshare) for the full list. Please see [YouTube Media Guidelines](/docs/media-guidelines/youtube) and [YouTube Authorization](/docs/dashboard/connect-social-accounts/youtube) for more information. ## Posting to YouTube ### Posting Overview Posting using the YouTube API requires the `youTubeOptions` object with a `title` parameter - max 100 Unicode code points. The `title` is the only required field and can be autogenerated with the [transcribe endpoint](/docs/apis/generate/transcribe-video). YouTube limits the title to 100 Unicode code points and the description (`post`) to 5000 bytes. When `youTubeOptions.shorts` is `true`, Ayrshare adds ` #shorts` (8 bytes) to the description, and that counts toward the 5000. Until **October 21, 2026**, Ayrshare shortens a title or description that is over the limit and adds a `warnings` entry (`code: 176`, with `feature` set to `title` or `description`) to the YouTube result. From October 21, 2026, a title or description over the limit returns error **176** with a `details` message that gives the length and the limit, and nothing is posted. A scheduled post that will publish on or after October 21, 2026 is checked when you submit it, so the error comes back right away instead of at the scheduled time. For example, to post a YouTube video with default settings: ```json YouTube Post theme={"system"} { // Required: Video description "post": "My Best YouTube Description", // empty string is allowed // Required: Platform to post to "platforms": ["youtube"], // Required: URL of video (only 1 allowed) "mediaUrls": ["https://img.ayrshare.com/012/vid.mp4"], "youTubeOptions": { // Required: Video title (max 100 characters) "title": "Your Best Title" } } ``` YouTube videos are `private` by default, but the visibility can be set to `public` or `unlisted`. Please see the optional fields below for more details. ### YouTube Post Optional Fields There are several other optional fields listed below, including the video `visibility`, `tags`, and `publishAt` date. Please see the comments for requirements and descriptions. ```json YouTube Post Optional Fields theme={"system"} { // Required fields "post": "My Best YouTube Description", // Video description, up to 5,000 bytes "platforms": ["youtube"], // Platform to post to "mediaUrls": ["https://img.ayrshare.com/012/vid.mp4"], // URL of video (1 allowed) "youTubeOptions": { // Required fields "title": "Your Best Title", // Video Title (max 100 characters) /** Optional Fields **/ // Visibility: "public", "unlisted", or "private" (default: "private") "visibility": "private", // Thumbnail settings - JPEG/PNG URL under 2MB, must end in png/jpg/jpeg "thumbNail": "https://img.ayrshare.com/012/gb.jpg", // Video organization "playListId": "PLrav6EfwgDX5", // Playlist ID to add the video "tags": ["dancing", "dogs"], // Tag array (400 chars total, 2+ chars each) // Video settings "madeForKids": false, // Self-declared kids content (default: false) "license": "youtube", // "youtube" (default) or "creativeCommon" "embeddable": true, // default: true "publicStatsViewable": true, // default: true "shorts": true, // Post as YouTube Short (max 3 minutes, adds #shorts) "notifySubscribers": true, // Send notification to subscribers (default: true) "categoryId": 24, // Video category (24 = Entertainment) "containsSyntheticMedia": true, // Disclose that a video contains realistic Altered or Synthetic (A/S) content // YouTube controlled publishing - UTC publish time. See below for details. "publishAt": "2022-10-08T21:18:36Z", } } ```
    • The `title` must be 100 Unicode code points or less. The `post` must be 5,000 bytes or less. Ayrshare replaces \< and > with \[ and ) before sending, since YouTube does not accept them.
    • The Playlist Id can be found by opening the playlist in a browser and copying the value after `list=`. The authenticated user and channel must be the owner of the playlist to add videos.
    • If your video doesn't end in a known video extension such as mp4, please use the `isVideo` parameter. See the [/post endpoint](/docs/apis/post/post) for details.
    • The `publishAt` field will allow YouTube to control the publishing time. The video will be private until the publish time when it will be made public. If the publish time is in the past, the video will be made public immediately. Do not use the post `scheduleDate` field when using the `publishAt` field.
    • The `containsSyntheticMedia` field is used to disclose that a video contains realistic Altered or Synthetic (A/S) content: Make a real person appear to say or do something they didn't actually say or do, alter footage of a real event or place, generate a realistic-looking scene that did not actually occur. See [AI Content Disclosure](/docs/apis/post/social-networks/youtube#ai-content-disclosure).
    • `license` - Sets the video license type. Accepted values: `"youtube"` (Standard YouTube License, default) or `"creativeCommon"` (Creative Commons - Attribution).
    • `embeddable` - Boolean (or the strings `"true"` / `"false"`). Controls whether the video can be embedded on third-party websites. Default: `true`.
    • `publicStatsViewable` - Boolean (or the strings `"true"` / `"false"`). Controls whether the **extended statistics panel** on the video's watch page is publicly viewable. The basic view count and like count remain publicly visible regardless of this setting. Default: `true`. See [`status.publicStatsViewable` in the YouTube Data API reference](https://developers.google.com/youtube/v3/docs/videos#status.publicStatsViewable) for details.
    • See [YouTube Media Guidelines](/docs/media-guidelines/youtube) for more information.
    > [!NOTE] > > **Monetization & Content Controls** > > The fields `madeForKids`, `license`, `embeddable`, and `publicStatsViewable` are settable via the Ayrshare API at upload time. However, direct monetization toggles (enabling/disabling ads), ad type selection (pre-roll, mid-roll, post-roll), revenue sharing, and Content ID require YouTube CMS credentials — available only to MCNs and enterprise content owners — and are **not** available through standard YouTube OAuth or the Ayrshare API. ## AI Content Disclosure Self-disclose realistic Altered or Synthetic (A/S) content so YouTube applies its AI disclosure to the published video. ```json YouTube AI Content Disclosure theme={"system"} { "post": "Generated with a little help from a model", "platforms": ["youtube"], "mediaUrls": ["https://img.ayrshare.com/012/vid.mp4"], "youTubeOptions": { "title": "Your Best Title", "containsSyntheticMedia": true } } ```
    • `containsSyntheticMedia`: Boolean set to `true` to disclose that the video contains realistic Altered or Synthetic content. Default `false`. Omitting the parameter means Ayrshare sends no disclosure with the upload. YouTube may still apply its own label (see below).
    • YouTube requires the disclosure when a video does any of the following: makes a real person appear to say or do something they didn't actually say or do, alters footage of a real event or place, or generates a realistic-looking scene that did not actually occur.
    • Only `true` and `"true"` turn the disclosure on. Any other value, including `false`, `"yes"`, or `1`, sends no disclosure and returns no warning.
    • It sets the **AI use** question on the video's Details page in YouTube Studio to **Yes**, so you can confirm it there after publishing.
    • This parameter is a self-disclosure only, and it is not the only way a label can appear. Ayrshare does not detect or infer whether your media is AI-generated. YouTube may also label a video on its own, for example content made with its GenAI tools or files carrying C2PA Content Credentials.
    > [!NOTE] > > Ayrshare cannot add the disclosure after a video publishes. Set `containsSyntheticMedia` at post > time. A **self-declared** disclosure can still be changed afterwards in YouTube Studio — but a > label YouTube applied itself, whether from its GenAI tools, from qualifying C2PA metadata, or > after manual review, is not editable there. ## YouTube Shorts A YouTube Short is a short-form vertical video that can be up to three minutes long. These are similar to TikTok videos and Instagram or Facebook Reels. ### Posting YouTube Shorts You can post a YouTube Shorts video of up to 3 minutes by adding the `shorts` parameter to the `youTubeOptions` object. ```json YouTube Shorts Post theme={"system"} { "youTubeOptions": { "shorts": true } } ``` The *#shorts* hashtag will be added to the YouTube description. ### Important Information on YouTube Shorts
    • Sending a video as a Short is an indication to YouTube that you would like the video to appear as a Short, but does not guarantee it will appear as a Short. The video must meet the [Short video](/docs/media-guidelines/youtube#shorts) requirements, such as being 3 minutes or less and vertical aspect ratio of 9:16 to be considered a Short by YouTube.
    • YouTube Shorts do not support thumbnails.
    • Additional information on using the [API to post YouTube Shorts](https://www.ayrshare.com/blog/post-youtube-shorts-with-an-api/).
    ## YouTube Thumbnails > [!NOTE] > > Custom thumbnails require a **verified YouTube channel**. The most common reason a thumbnail fails to apply (while the video still posts) is an unverified channel — verify at [https://www.youtube.com/verify](https://www.youtube.com/verify) (phone verification). Your `thumbNail` must also be a **PNG or JPG/JPEG**, **2MB or less**, and served from a **reachable URL**; Ayrshare validates these before publishing where possible. When a video posts but the thumbnail fails, the YouTube result keeps `status: "success"` and adds a `warnings` array (`feature: "thumbnail"`, `code: 307`) describing the failure. See [YouTube Thumbnail Not Applied (Unverified Channel)](/docs/help-center/technical-support/youtube-thumbnail-unverified-channel) for the full fix. ### Adding YouTube Thumbnails YouTube Thumbnails and other features, such as uploading 15 minute videos, require verification of your phone number. A `thumbNail` is a URL of a JPEG or PNG and less than 2MB in size. The file extension must end in png, jpg, or jpeg. ```json YouTube Thumbnail theme={"system"} { "youTubeOptions": { "thumbNail": "https://img.ayrshare.com/012/gb.jpg" } } ``` YouTube Shorts do not currently support thumbnails. ### Enable Thumbnails in YouTube Studio You must be granted YouTube permissions to post thumbnails. In [YouTube Studio](https://studio.youtube.com/) go to *Settings->Channel*. Select "*Feature Eligibility*" and click "*Features that require phone verification*". Enter your phone number to enable. ![YouTube Studio](https://mintcdn.com/ayrshare-docs/Nmrhj2Gh7WSf62Bh/images/apis/post/yt-thumb.webp?fit=max\&auto=format\&n=Nmrhj2Gh7WSf62Bh\&q=85\&s=67855a44b4ab5612d5f89df9db511556) > [!WARNING] > > YouTube may take up to 24 hours to enable thumbnails after you verify your phone number. Please note, YouTube determines eligibility for adding thumbnails. "Enabled" phone verification does not guarantee YouTube will allow thumbnail uploads. > > If you have been verified for 24 hours and still have issues check that: > > 1. You are able to manually upload thumbnails in YouTube Studio. > 2. If you're working with a Brand Content Owner Account (often used for business or organization channels), make sure you have the necessary permissions. We recommend "Owner" rights. > 3. If you are still having issues, please see this video on [solving the Thumbnail issue](https://www.youtube.com/watch?v=1bFmX2uQk0Y). ## Video: Posting to YouTube via the API [Posting to YouTube API](https://www.youtube.com/embed/UkisNgVxubg) ## Upload Limits YouTube limits how many videos a channel can upload in a 24-hour period via the YouTube API.
    • Depending on a creator's location, a channel might be able to increase their daily limit by getting access to advanced features. To learn more visit this [article](https://notifications.google.com/g/p/ACnX6LbjUElgT-OfRLVjenkpdldv6pIJ5JYGZyPGTdJwRYmo3fXFkfXJsPGnswgns0BjJ6Bjxn2bqpqfBt6gBdkLoqESKA7mqLNllzcR2qnYUJn5KlJN5jPqCWl6DGr58cZEt94mMvxXATN6_PaBR1RYEpNMTYWN).
    • Limits may vary by country/region or channel history. Copyright strikes may impact channel history eligibility and [Community Guidelines strikes](https://support.google.com/youtube/answer/2802032) will affect how much a channel can upload.
    If you receive an upload limit error please wait and try again in 24 hours. ## Description Links You must activate **Advanced Features** in YouTube Studio to have clickable links in the YouTube video description. Go to **YouTube Studio -> Settings -> Channel -> Feature Eligibility -> Advanced Features -> Access Features** to activate the advanced features. ![YouTube Feature Eligibility](https://mintcdn.com/ayrshare-docs/Nmrhj2Gh7WSf62Bh/images/apis/post/YT-settings.webp?fit=max\&auto=format\&n=Nmrhj2Gh7WSf62Bh\&q=85\&s=129f12886c2896167f936dcc7c633234) ## Playlists You can add videos to a YouTube playlist by including the `playListId` in the `youTubeOptions` object. Be sure that the authenticated user and channel are the owner of the playlist. ```json YouTube Playlist theme={"system"} { "youTubeOptions": { "playListId": "PLrav6EfwgDX5" } } ``` ## Subtitles / Captions for Videos You can add YouTube subtitles, also known as YouTube captions, to videos by including an [SRT file](https://en.wikipedia.org/wiki/SubRip) or a YouTube [SBV file](https://support.google.com/youtube/answer/2734698). Use the `subTitleUrl` field in the `youTubeOptions` object to specify the URL to your SRT or SBV file. ```json YouTube Subtitles theme={"system"} { "youTubeOptions": { "title": "My new post from Ayrshare to Youtube", "subTitleUrl": "https://img.ayrshare.com/012/captions.srt", "subTitleLanguage": "en", "subTitleName": "English" } } ```
    • `subTitleUrl`: A valid SRT or SBV file. The URL must start with `https://` and end in `.srt` or `.sbv` and be a valid SRT or SBV file. The file must be under 100 MB.
    • `subTitleLanguage`: Optional: The language of the subtitles. Must be a valid [language code](/docs/iso-codes/language). Default: "en".
    • `subTitleName`: Optional: The name of the caption track. The name is intended to be visible to the user as an option during playback. The maximum name length supported is 150 characters. Default: "English".
    > [!NOTE] > > **What are SRT and SBV files?** > > SRT (SubRip Subtitle) and SBV (YouTube SubViewer) are subtitle file formats used for displaying > timed text in videos. The main difference is that SRT uses timestamps in HH:MM:SS,MS format with > arrow separators, while SBV uses HH:MM:SS.MS format with commas. > > ```text theme={"system"} > ## SRT Format Example > 1 > 00:00:01,000 --> 00:00:04,000 > Welcome to our tutorial on subtitle formats. > > 2 > 00:00:04,500 --> 00:00:08,000 > Today we'll learn about SRT and SBV files. > > ## SBV Format Example > 0:00:01.000,0:00:04.000 > Welcome to our tutorial on subtitle formats. > > 0:00:04.500,0:00:08.000 > Today we'll learn about SRT and SBV files. > ``` ## Tags You can add YouTube tags to your videos by including the `tags` array in the `youTubeOptions` object. Tags must be at least 2 characters in length each and the total length of all tags must be 400 characters or less. ```json YouTube Tags theme={"system"} { "youTubeOptions": { "tags": ["dancing", "dogs"] } } ``` ## YouTube Mentions While you can add a `@handle` to a YouTube post, YouTube does not support resolving mentions in the post text. The `@handle` will remain as plain text. ## Character Limits Please see [YouTube Character Limits](/docs/help-center/technical-support/character-limits#youtube-character-limits) for more information. ## Unlinking and Revoked Access If a user removes Ayrshare's access in their Google account, the next YouTube request returns error **250** and Ayrshare unlinks YouTube from that profile. You receive a [social webhook](/docs/apis/webhooks/actions#social-action) with `"type": "unlink"` and `"source": "system"`, and an email is sent. Relink YouTube to continue posting. When YouTube is unlinked, whether by you, your user, or automatically, Ayrshare: * Revokes its access to the Google account, unless the same Google account is still linked to YouTube on another profile, or Ayrshare cannot confirm that it isn't. * Deletes the YouTube data it stored for that profile 5 days later: YouTube analytics, records of comments sent through Ayrshare, and the YouTube video IDs and URLs in your post history. Your posts stay in your post history without their YouTube links, and the videos stay on YouTube. YouTube's API policies require this deletion within 7 days. If the same Google account is linked to YouTube on that profile again before then, the data is kept. Relinking after the deletion does not restore it. ## Additional Endpoints > [card] [Get YouTube Categories](/docs/apis/utils/youtube-categories) > [card] [Set YouTube Watermark](/docs/apis/utils/set-youtube-watermark) > [card] [Remove YouTube Watermark](/docs/apis/utils/remove-youtube-watermark) # Update a Post Source: https://www.ayrshare.com/docs/apis/post/update-post PATCH /post Reschedule, pause, or add notes to a pending post, approve a post awaiting approval, or disable comments and change YouTube visibility once published. Update the `scheduleDate` of a post, `approval` status of a post, `notes`, or `visibility` of a posted YouTube video.
    • The post must originally have a `scheduleDate` and be in a "pending" `status`. The `status` can be checked with the /history or GET /post endpoints.
    • The YouTube video must have been successfully posted to change visibility. Send `visibility` inside `youTubeOptions`, not at the top level of the body.
    • Besides `id`, the request must include at least one of the parameters below. A request with only `id` returns `code: 101`.
    • The [approval workflow](/docs/apis/post/overview#approval-workflow) requires the current status of the post be in "awaiting approval".
    Other parameters cannot be updated. You will need to delete the post and repost. ## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` **Profile-Key** `string` [Profile Key](/docs/apis/overview#profile-key-format) of a User Profile. Format: `Profile-Key: PROFILE_KEY` ## Body Parameters **id** `string` *required* Ayrshare Post ID of the post to update. Ayrshare Post ID returned from [/post](/docs/apis/post/post). **approved** `boolean` (default: false) The original post requires approval and has a status of "awaiting approval", set to `true` to approve and publish the post. **disableComments** `boolean` (default: false) Enable or disable comments on a post. Setting to `true` will disable comments. Setting to `false` will enable comments. Supported platforms: Instagram and LinkedIn.
    • The post must be published. Otherwise the request returns error `305`.
    • Disabling comments on a published post will not delete existing comments.
    • Disabling LinkedIn comments will delete all existing comments on the thread.
    • Instagram comments will not be deleted.
    • TikTok comments cannot be changed after publishing.
    **notes** `string` Set notes on a post that can be retrieved via the [/history](/docs/apis/history/overview) endpoint. Notes are for reference only and do not affect the post. **scheduleDate** `string` The `datetime` to schedule a future post. Accepts a UTC date time. For example, use format `YYYY-MM-DDThh:mm:ssZ` and send as `2026-07-08T12:30:00Z`. Please see [utctime](https://www.utctime.net/) for more examples. > [!WARNING] > > If the datetime is in the past, the post will immediately be sent. **scheduledPause** `boolean` (default: false) Pause or unpause a scheduled post. If a post is unpaused and the `scheduleDate` is in the past, the post will immediately be publish. Consider updating the `scheduleDate` before unpausing. **youTubeOptions** `object` Update the visibility of the YouTube video with the `visibility` field and values `unlisted`, `private`, or `public`. Update the `description`, `title`, or `categoryId`. If no `description` or `categoryId` originally set, the default values are `""` and `24` (Entertainment), respectively. ```bash cURL theme={"system"} curl \ -H "Authorization: Bearer API_KEY" \ -H 'Content-Type: application/json' \ -d '{"id": "s8k2jsk0pl", "scheduleDate": "2023-07-08T12:30:00Z", scheduledPause: true}' \ -X PATCH https://api.ayrshare.com/api/post ``` ```javascript JavaScript theme={"system"} const API_KEY = "API_KEY"; fetch("https://api.ayrshare.com/api/post", { method: "PATCH", headers: { "Content-Type": "application/json", "Authorization": `Bearer ${API_KEY}` }, body: JSON.stringify({ id: "s8k2jsk0pl", // required scheduleDate: "2023-07-08T12:30:00Z", scheduledPause: true }), }) .then((res) => res.json()) .then((json) => console.log(json)) .catch(console.error); ``` ```python Python theme={"system"} import requests payload = {'id': 's8k2jsk0pl', 'scheduleDate': '2023-07-08T12:30:00Z'} headers = {'Content-Type': 'application/json', 'Authorization': 'Bearer API_KEY'} r = requests.patch('https://api.ayrshare.com/api/post', json=payload, headers=headers) print(r.json()) ``` ```php PHP theme={"system"} 's8k2jsk0pl', // Replace with your actual post ID 'scheduleDate' => '2023-07-08T12:30:00Z' ]); $curl = curl_init($apiUrl); curl_setopt_array($curl, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_CUSTOMREQUEST => 'PUT', CURLOPT_HTTPHEADER => $headers, CURLOPT_POSTFIELDS => $data ]); $response = curl_exec($curl); if ($response === false) { echo 'Curl error: ' . curl_error($curl); } else { echo json_encode(json_decode($response), JSON_PRETTY_PRINT); } curl_close($curl); ``` ```csharp C# theme={"system"} using System; using System.Net.Http; using System.Text; using System.Threading.Tasks; namespace PostUpdatePOSTRequest_csharp { class PostUpdate { static async Task Main(string[] args) { string API_KEY = "API_KEY"; string url = "https://api.ayrshare.com/api/post"; using (var httpClient = new HttpClient()) { try { httpClient.DefaultRequestHeaders.Add("Authorization", "Bearer " + API_KEY); string json = "{\"id\":\"s8k2jsk0pl\"," + "\"scheduleDate\":\"2023-07-08T12:30:00Z\"}"; var content = new StringContent(json, Encoding.UTF8, "application/json"); var response = await httpClient.PatchAsync(url, content); var responseBody = await response.Content.ReadAsStringAsync(); response.EnsureSuccessStatusCode(); Console.WriteLine(responseBody); } catch (HttpRequestException ex) { Console.WriteLine("Error: " + ex.Message); if (ex.InnerException != null) { Console.WriteLine("Error details: " + ex.InnerException.Message); } } } } } } ``` ```go Go theme={"system"} package main import ( "bytes" "encoding/json" "log" "net/http" ) func main() { message := map[string]interface{}{ "id": "s8k2jsk0pl", "scheduleDate": "2023-07-08T12:30:00Z" } bytesRepresentation, err := json.Marshal(message) if err != nil { log.Fatalln(err) } req, _ := http.NewRequest("PATCH", "https://api.ayrshare.com/api/post", bytes.NewBuffer(bytesRepresentation)) req.Header.Add("Content-Type", "application/json; charset=UTF-8") req.Header.Add("Authorization", "Bearer API_KEY") res, err := http.DefaultClient.Do(req) if err != nil { log.Fatal("Error:", err) } res.Body.Close() } ``` ```json 200: Success theme={"system"} { "status": "success", "id": "ZSU1tnnuykDy25wA6kvX", // Ayrshare Post ID "scheduleDate": "2025-07-08T12:30:00Z", "scheduledPaused": true } ``` ```json 400: ID Not Found theme={"system"} { "action": "update", "status": "error", "code": 305, "message": "Error updating post. Post ID not found." }on ``` ```json 400: Invalid scheduleDate theme={"system"} { "action": "post", "status": "error", "code": 104, "message": "Invalid schedule date format for scheduleDate. .../ayrshare.com/rest-api/endpoints/post#send-a-post" } ``` # Create a Link Session Source: https://www.ayrshare.com/docs/apis/profiles/create-link-session POST /profiles/link-sessions Create a social-linking URL for a user profile, without sending a private key. Create a social-linking URL for a User Profile. Send the returned `url` to your user, and they open it to connect their social accounts. This is the recommended way to create a linking URL. It needs only your API key and a `Profile-Key` — there is no private key to send and nothing to sign. Unlike a linking URL created before, a link session is stored, so you can check whether it has been used and revoke it before it expires. The returned `url` signs your user into their profile, so treat it like a password and send each one to a single user. See [Sending the Linking URL](/docs/apis/profiles/social-linking-overview#sending-the-linking-url). > [!NOTE] > > The URL is valid for **5 minutes** by default. Use `expiresIn` to set a different > window, up to 2880 minutes (48 hours). > [!NOTE] > > [Generate a Linking URL](/docs/apis/profiles/generate-jwt) performs the same operation and keeps > working unchanged. It accepts the legacy `privateKey`, `base64` and `verify` parameters > and ignores them. `domain` is not ignored on either endpoint - it stays optional and is > still validated. New integrations should use this endpoint. > > One difference in the response: `generateJWT` returns a top-level `token` for backwards > compatibility, and this endpoint does not return one **alongside** a `url` — the token in a > `url` lives inside it. If you are migrating and your code reads `token`, read `url` instead. > ([Connect mode](#connect-mode) for the embedded widget is the one shape that returns a bare > `token`, because it returns no URL for the token to live in.) ## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` **Profile-Key** `string` *required* [Profile Key](/docs/apis/overview#profile-key-format) of a User Profile. Format: `Profile-Key: PROFILE_KEY` > [!NOTE] > > The `Profile-Key` is a header on this endpoint — there is no `profileKey` body > parameter. If it is missing you get `code: 188`, whose message lists `privateKey`, > `profileKey` and other legacy field names because it is shared with > [Generate a Linking URL](/docs/apis/profiles/generate-jwt). Read it as "the Profile-Key header is > missing or wrong"; none of the other names in it are parameters of this endpoint. **X-Twitter-OAuth1-Api-Key** `string` Your X API Key (Consumer Key) from the X Developer Portal. When provided, the linking URL will use your own X Developer App for OAuth linking. **X-Twitter-OAuth1-Api-Secret** `string` Your X API Secret (Consumer Secret) from the X Developer Portal. Required when `X-Twitter-OAuth1-Api-Key` is provided. ## Body Parameters **mode** `string` (default: grid) Which linking surface this session drives. * `grid` — the hosted linking page, showing every network you permit. This is the default, so a request that omits `mode` creates one. * `connect` — one network at a time, opened from your own dashboard. See [Connect Mode](#connect-mode) below and [Direct Mode](/docs/multiple-users/connect-direct-mode). Never inferred: passing `origin` or `network` does not put you in connect mode, so a grid session cannot become a gated one by accident. Any other value returns `code: 188` with `details` naming the two. **expiresIn** `number` (default: 5) Longevity of the link in minutes. Range: 1 to 2880 minutes. Requires the Max Pack. See [Link Expiry](/docs/apis/profiles/social-linking-overview#jwt-expires-in) for more information. **logout** `boolean` (default: false) Automatically log out the current session. Not recommended in production, since it affects performance. See [Automatic Logout of a Profile Session](/docs/multiple-users/api-integration-business#automatic-logout-of-a-profile-session). **redirect** `string` A URL to redirect to when the "Done" button or logo image is clicked. Add the query parameter `origin=true` to redirect the opener window. **allowedSocial** `array` The social networks to display on the linking page. Overrides the networks configured on the [Social Networks](/docs/multiple-users/manage-user-profiles#set-social-networks-access) page. ```json Only display Facebook, X/Twitter, LinkedIn, and TikTok theme={"system"} { "allowedSocial": ["facebook", "twitter", "linkedin", "tiktok"] } ``` **network** `string` Connect mode only. The single social network this session connects, which is what makes it a **direct-mode** session. Omit it for a session your own dashboard drives across several networks. One of `bluesky`, `facebook`, `gmb`, `instagram`, `instagramApi`, `linkedin`, `pinterest`, `reddit`, `snapchat`, `telegram`, `threads`, `tiktok`, `twitter`, `whatsapp`, `x`, `youtube`. Anything else returns `code: 508` — including `fbg`, which is not a link target here. Cannot be combined with `allowedSocial` (`code: 507`): a single-network session is already its own allowlist. A network your account has not enabled returns `code: 509`, which is a different answer from 508 because it is fixable on your [Social Networks](/docs/multiple-users/manage-user-profiles#set-social-networks-access) page. In grid mode it is ignored. **instagramLinkMethod** `string` Override which Instagram linking flow is used for this link. Valid values: * `instagram`: Direct Instagram Login, no Facebook Page required. * `facebook`: Link Instagram via a connected Facebook Page. When omitted, the linking page uses your account-wide [Instagram Login](/docs/multiple-users/manage-user-profiles#instagram-login) setting. **origin** `string` The exact origin of the page that opened the linking window, so it can be told when linking finishes. When set, the linking page posts events to that origin with `window.postMessage` as your user connects each account, and your page can react without polling. Events are only ever sent to this exact value, so it must match your page's origin character for character, including the scheme and any port. Three shapes are accepted: an `https` origin (`https://app.example.com`), `http://localhost:3000` for local development, and a native custom scheme (`myapp://connected`). Anything else — a plain `http://` origin other than localhost, or something that is not an origin at all — is ignored on the links this endpoint creates: the link still works, it simply sends no events. It is optional, so omitting it is not an error either. Of the three, only the first two receive events. A custom scheme is a return target for a mobile app and **cannot receive them**, because there is no browser window to post to; native apps poll [Get a Link Session](/docs/apis/profiles/get-link-session) instead. See [Link Completion Events](/docs/multiple-users/link-completion-events). **In connect mode `origin` is required, and it is checked.** The leniency above is grid-mode behaviour. With `mode: "connect"`, omitting it returns `code: 505` and a value that is not one of the three accepted shapes returns `code: 506`, whose `details` repeat the shape you sent. **domain** `string` Optional. Your linking domain, when your account has more than one. When omitted, your account's own domain is used. A domain not registered to your account is rejected. **email** `object` Send a Connect Accounts email carrying the link, so your user can reach their linking page directly. Requires a `to` address. Requires the Max Pack. The response reports the outcome in `emailSent`. If the email can't be sent, you still get the link: the response has `emailSent: false` and an `emailError` object, so you can send the link to your user another way. See [Connect Accounts Email](/docs/apis/profiles/social-linking-overview#connect-accounts-email). ## Connect Mode `mode: "connect"` creates a session for a linking surface you host yourself, rather than for the hosted linking page. Which of the two connect shapes you get depends on one thing — whether you pass `network`: | You send | You get back | What you do with it | | - | - | - | | `mode: "connect"` and a `network` | a `url` pointing at a single-network connect page | open it in a popup — this is [direct mode](/docs/multiple-users/connect-direct-mode) | | `mode: "connect"` and no `network` | a `token`, and no URL at all | hand it to your own front end | > [!NOTE] > > **The response carries the secret exactly once.** A response has either a `url` or a `token`, > never both and never two URLs. The token in a direct-mode session lives inside the `url`, the > same way it does in grid mode; a session with no URL to carry it returns the bare `token` > instead. Everything else is the same in all three modes: `sessionId`, `expiresAt`, `emailSent`, > and `title` when the User Profile has one. ### What Connect Mode Requires Neither of these is a body field of its own — the first is an account entitlement and the second is the [`origin`](#body-parameters) parameter above, which connect mode makes mandatory. **The [Max Pack](/docs/additional/maxpack).** Without it the call returns `code: 504`, checked before the connect-mode parameters, so correcting `origin` or `network` will not change the answer. Contact support if you need connect mode enabled on an account without the Max Pack. **An `origin`, on every session.** There is no allowlist and no registration step — you send it on each call and it is stored on the session, so a new environment needs no setup on our side. Three shapes are accepted: * an `https` origin — `https://app.example.com` * a native custom scheme — `myapp://connected` * `http://localhost` or `http://localhost:3000`, for local development Origin only: no path, query or fragment, and no credentials in it. Omitting it returns `code: 505`, and anything that is not one of the three shapes returns `code: 506`. > [!WARNING] > > `email` cannot be used with a session that has no `network`, because there is no link to put in > the email — that shape returns a token for your own front end. The call returns `code: 510`. > Add a `network` for a direct-mode session, which does have a URL, or omit `email`. Every code named above is in the [Link Session Errors](/docs/errors/errors-ayrshare#link-session-errors) reference. ```bash cURL theme={"system"} curl \ -H "Authorization: Bearer API_KEY" \ -H 'Content-Type: application/json' \ -H 'Profile-Key: PROFILE_KEY' \ -d '{"expiresIn": 60}' \ -X POST https://api.ayrshare.com/api/profiles/link-sessions ``` ```javascript JavaScript theme={"system"} const API_KEY = "API_KEY"; const PROFILE_KEY = "PROFILE_KEY"; fetch("https://api.ayrshare.com/api/profiles/link-sessions", { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${API_KEY}`, "Profile-Key": PROFILE_KEY, }, body: JSON.stringify({ expiresIn: 60 }), }) .then((res) => res.json()) .then((json) => console.log(json)) .catch(console.error); ``` ```python Python theme={"system"} import requests payload = {'expiresIn': 60} headers = {'Content-Type': 'application/json', 'Authorization': 'Bearer API_KEY', 'Profile-Key': 'PROFILE_KEY'} response = requests.post('https://api.ayrshare.com/api/profiles/link-sessions', json=payload, headers=headers) print(response.json()) ``` ```php PHP theme={"system"} request( 'POST', 'https://api.ayrshare.com/api/profiles/link-sessions', [ 'headers' => [ 'Content-Type' => 'application/json', 'Authorization' => 'Bearer API_KEY', 'Profile-Key' => 'PROFILE_KEY' ], 'json' => [ 'expiresIn' => 60, ] ] ); echo json_encode(json_decode($res->getBody()), JSON_PRETTY_PRINT); ``` ```csharp C# theme={"system"} using System; using System.Net.Http; using System.Text; using System.Threading.Tasks; using Newtonsoft.Json; namespace CreateLinkSession_csharp { class CreateLinkSession { static async Task Main(string[] args) { string API_KEY = "API_KEY"; string PROFILE_KEY = "PROFILE_KEY"; string url = "https://api.ayrshare.com/api/profiles/link-sessions"; try { using (var client = new HttpClient()) { client.DefaultRequestHeaders.Add("Authorization", "Bearer " + API_KEY); client.DefaultRequestHeaders.Add("Profile-Key", PROFILE_KEY); var sendData = new { expiresIn = 60 }; string jsonData = JsonConvert.SerializeObject(sendData); var content = new StringContent(jsonData, Encoding.UTF8, "application/json"); HttpResponseMessage response = await client.PostAsync(url, content); response.EnsureSuccessStatusCode(); string responseBody = await response.Content.ReadAsStringAsync(); Console.WriteLine(responseBody); } } catch (HttpRequestException e) { Console.WriteLine($"HTTP request error: {e.Message}"); } } } } ``` ```json 200 theme={"system"} { "status": "success", "sessionId": "c7a2434e72e91bde27579efde0fd6dd0b74ceee29a471fd368407f273708c2e8", // Identifier for this link. Use it with Get and Revoke a Link Session. "url": "https://profile.ayrshare.com?session=ayr_ls_kJ8mQ2vX9pLnR4tYwZ6aBcD1eFgH3iJkLmN0oPqRsTu&domain=YOUR_DOMAIN", // Send this to your user exactly as returned. The token exists only in here. "expiresAt": "2026-09-02T08:03:26.838Z", // When the link stops working, as an ISO 8601 timestamp. "emailSent": false, // Whether the connect-accounts email was sent. false with no emailError means no email was requested. "title": "Acme Client" // The User Profile's title. Omitted when the profile has none. } ``` ```json 200: Email Failed to Send theme={"system"} { "status": "success", "sessionId": "c7a2434e72e91bde27579efde0fd6dd0b74ceee29a471fd368407f273708c2e8", "url": "https://profile.ayrshare.com?session=ayr_ls_kJ8mQ2vX9pLnR4tYwZ6aBcD1eFgH3iJkLmN0oPqRsTu&domain=YOUR_DOMAIN", // Still valid. Send it to your user yourself. "expiresAt": "2026-09-02T08:03:26.838Z", "emailSent": false, "emailError": { // Only present when an email was requested and could not be sent. "code": 333, "message": "Email failed to send. Please confirm a valid email was provided." }, "title": "Acme Client" } ``` ```json 200: Direct Mode (mode: "connect" with a network) theme={"system"} { "status": "success", "sessionId": "c7a2434e72e91bde27579efde0fd6dd0b74ceee29a471fd368407f273708c2e8", "url": "https://profile.ayrshare.com/connect?session=ayr_ls_kJ8mQ2vX9pLnR4tYwZ6aBcD1eFgH3iJkLmN0oPqRsTu", // Open this in a popup. One network, no domain parameter. "expiresAt": "2026-09-02T08:03:26.838Z", "emailSent": false, "title": "Acme Client" } ``` ```json 200: Connect Mode Without a Network theme={"system"} { "status": "success", "sessionId": "c7a2434e72e91bde27579efde0fd6dd0b74ceee29a471fd368407f273708c2e8", "token": "ayr_ls_kJ8mQ2vX9pLnR4tYwZ6aBcD1eFgH3iJkLmN0oPqRsTu", // No url: this shape returns the bare token instead. Treat it like a password. "expiresAt": "2026-09-02T08:03:26.838Z", "emailSent": false, // Always false here - email needs a link to send, so it returns code: 510. "title": "Acme Client" } ``` ```json 401: Connect Mode Requires the Max Pack theme={"system"} { "action": "link session", "status": "error", "code": 504, "message": "Connect mode requires the Max Pack. Activate it on your Account page: https://app.ayrshare.com/account" } ``` ```json 400: Missing Profile-Key Header theme={"system"} { "action": "JWT", "status": "error", "code": 188, "message": "Missing or incorrect privateKey, profileKey, domain, email 'to', or expiresIn fields." } ``` ```json 400: Domain Not Registered to Your Account theme={"system"} { "action": "JWT", "status": "error", "code": 189, "message": "Error generating JWT. Check the sent parameters.", "details": "Missing or incorrect domain." } ``` ```json 401: expiresIn Requires the Max Pack theme={"system"} { "action": "authorization", "status": "error", "code": 340, "message": "Max Pack is required. Please go to your Account page in the Ayrshare Dashboard to activate the Max Pack.", "details": "email and expiresIn parameters are only available on the Max Pack." } ``` # Create a User Profile Source: https://www.ayrshare.com/docs/apis/profiles/create-profile POST /profiles Create a new User Profile under your Primary Profile. The response includes the Profile Key, which you need to securely store to make API calls for users. Create a new profile under your Primary Profile. Upon successful creation of the User Profile, the API response will include the Profile Key associated with the newly created profile. Securely store the Profile Key in your system, as it will be required to make API calls on behalf of your user. Please note that for security reasons, the Profile Keys are only returned in two specific cases: 1. User Profile Creation: When a new User Profile is created. 2. In the Ayrshare Dashboard: You can find the Profile Key for each profile within the Ayrshare Dashboard. Switch to the User Profile and then navigate to the Profile Key page. We recommend you securely store the Profile Key in your system. ## Manage Social Networks Use the disableSocial field to manage (add/remove) social networks from display. Please see [Manage Social Accounts](/docs/apis/profiles/overview#enable-or-disable-social-networks) for more information. ## Manage Active User Profiles Please see here for some recommendations on [managing active User Profiles](/docs/multiple-users/manage-user-profiles#managing-active-user-profiles). ## Important Security Considerations > [!WARNING] > >
      >
    • Avoid sharing Profile Keys publicly or exposing them in client-side code or public repositories.
    • >
    • Profile Keys are sensitive credentials used to authenticate and authorize access to User Profiles.
    • >
    • It is crucial to store Profile Keys securely in your system with appropriate access controls to maintain the integrity and confidentiality of your users' data.
    • >
    • For security reasons, the Profile Key can't be retrieved again using the API. However, you can retrieve the Profile Key from the [dashboard](/docs/multiple-users/manage-user-profiles#get-the-profile-key).
    • >
    • The `refId` should also be stored to associate a profile to an endpoint return.
    • >
    ## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` ## Body Parameters **title** `string` *required* Title of the new profile. Must be unique. This title will be displayed on the social account linking page. **messagingActive** `boolean` (default: false) Set to true to activate messaging for this user profile. Messaging must first be enabled for your Ayrshare account. **hideTopHeader** `boolean` (default: false) Hide the top header on the social accounts linkage page. **hideLogo** `boolean` (default: false) Hide the company logo on the social accounts linkage page for this user profile only. The account-wide logo on other profiles is unaffected. Useful for white-labeling individual partner profiles while keeping branding on the rest. **topHeader** `string` Change the header on the social accounts linkage page. If not set, then displays: "Social Accounts for "title" where "title" is the profile title. **disableSocial** `array` Array of social networks that are disabled for this user's profile. The primary profile's list of disabled social networks takes precedence. Available networks: `bluesky`, `facebook`, `gmb`, `instagram`, `linkedin`, `pinterest`, `reddit`, `snapchat`, `telegram`, `threads`, `tiktok`, `twitter`, and `youtube`. See [enable or disable social networks](/docs/apis/profiles/overview#enable-or-disable-social-networks) for more information. **team** `boolean` (default: false) Create a new user profile as a team member by setting `team: true`. The `email` field will be used to send an invite email. See [inviting a team member](/docs/multiple-users/manage-user-profiles#invite-a-team-member) for details on the requirements. **email** `string` A valid email address where the team member invite will be sent. Required if `team: true` **subHeader** `string` Change the sub header on the social accounts linkage page. Currently displays "Click an icon to link a social network". Set to an empty string to remove. See how to change the [help link](/docs/multiple-users/manage-user-profiles#help-links-visible). **tags** `array` Tag user profiles using an array of strings. These tags serve as an internal organizational tool to categorize and manage your user profiles effectively. **demo** `boolean` (default: false) Create the profile as a [Demo User Profile](/docs/apis/profiles/overview#demo-user-profiles) — a 30-day evaluation profile that automatically converts to a standard User Profile after 30 days. Demo User Profiles are available on the Enterprise plan and must be enabled for your account — [contact us](mailto:support@ayrshare.com) if you'd like to learn more. Returns HTTP 403 with error code 480 if not enabled. ```javascript cURL theme={"system"} curl \ -H "Authorization: Bearer API_KEY" \ -H 'Content-Type: application/json' \ -d '{"title": "ACME Profile"}' \ -X POST https://api.ayrshare.com/api/profiles ``` ```javascript JavaScript theme={"system"} const API_KEY = "API_KEY"; fetch("https://api.ayrshare.com/api/profiles", { method: "POST", headers: { "Content-Type": "application/json", "Authorization": `Bearer ${API_KEY}` }, body: JSON.stringify({ title: "ACME Profile", // required }), }) .then((res) => res.json()) .then((json) => console.log(json)) .catch(console.error); ``` ```python Python theme={"system"} import requests payload = {'title': 'ACME Profile'} headers = {'Content-Type': 'application/json', 'Authorization': 'Bearer API_KEY'} r = requests.post('https://api.ayrshare.com/api/profiles', json=payload, headers=headers) print(r.json()) ``` ```php PHP theme={"system"} request( 'POST', 'https://api.ayrshare.com/api/profiles', [ 'headers' => [ 'Content-Type' => 'application/json', 'Authorization' => 'Bearer API_KEY' ], 'title' => ['ACME Profile'], ] ); echo json_encode(json_decode($res->getBody()), JSON_PRETTY_PRINT); ``` ```go Go theme={"system"} package main import ( "bytes" "encoding/json" "log" "net/http" ) func main() { message := map[string]interface{}{ "title": "ACME Profile" } bytesRepresentation, err := json.Marshal(message) if err != nil { log.Fatalln(err) } req, _ := http.NewRequest("POST", "https://api.ayrshare.com/api/profiles", bytes.NewBuffer(bytesRepresentation)) req.Header.Add("Content-Type", "application/json; charset=UTF-8") req.Header.Add("Authorization", "Bearer API_KEY") res, err := http.DefaultClient.Do(req) if err != nil { log.Fatal("Error:", err) } res.Body.Close() } ``` ```csharp C# theme={"system"} using System; using System.Net.Http; using System.Text; using System.Threading.Tasks; namespace CreateProfilePOSTRequest_csharp { class CreateProfile { static async Task Main(string[] args) { string API_KEY = "API_KEY"; string url = "https://api.ayrshare.com/api/profiles"; string json = "{\"title\": \"ACME Profile\"}"; using (var client = new HttpClient()) { client.DefaultRequestHeaders.Add("Authorization", "Bearer " + API_KEY); try { var content = new StringContent(json, Encoding.UTF8, "application/json"); HttpResponseMessage response = await client.PostAsync(url, content); response.EnsureSuccessStatusCode(); string responseBody = await response.Content.ReadAsStringAsync(); Console.WriteLine(responseBody); } catch (HttpRequestException e) { Console.WriteLine($"Error: {e.Message}"); } } } } } ``` ```json 200: Success theme={"system"} { "status": "success", "title": "Digg It", "refId": "140b8709bd6ade099b242d895e268fb886130c53", "profileKey": "7TVRLEZ-24A43C0-NJW0Z82-F11984N", "messagingActive": true // true if messaging for this user profile is active } ``` ```json 400: Profile Title Already Exists theme={"system"} { "action": "create", "status": "error", "code": 146, "message": "Profile title already exists." } ``` ```json 403: Demo Profiles Not Enabled theme={"system"} { "action": "create profiles", "status": "error", "code": 480, "message": "Demo profiles are not enabled for this account. Demo profiles require an Enterprise plan and must be enabled by Ayrshare." } ``` # Delete a User Profile Source: https://www.ayrshare.com/docs/apis/profiles/delete-profile DELETE /profiles Permanently delete a User Profile you own and all associated posts by Profile Key or title. Learn about rate limits and how deletion affects billing. Delete a user profile you are the owner of. The Profile Key in the header parameter is the User Profile to be deleted. > [!WARNING] > > Deleting a user profile permanently removes the profile and all associated posts—this action is > irreversible and cannot be undone. You can delete up to 8 user profiles per second, so please > stagger your API calls when performing bulk deletions to stay within rate limits. > [!NOTE] > > Each monthly billing period is charged on the highest number of user profiles reached on any > single day of that period, and never on fewer than the minimum included in your plan — deleting > profiles while you are at that minimum does not lower the charge. That high-water mark only ever > rises: deleting a profile stops later days from pushing it higher, but it cannot bring down a > figure the period has already reached, so once your peak is behind you the saving lands in your > next billing cycle. This does not apply to the Launch plan, which has a flat price, or to custom > and Enterprise agreements, whose own terms apply. To see your daily user profile counts, call > [Get User Profiles](/docs/apis/profiles/get-profiles) with `actionLog` set to `true`. ## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` **Profile-Key** `string` *required* [Profile Key](/docs/apis/overview#profile-key-format) of a User Profile. Format: `Profile-Key: PROFILE_KEY` ## Body Parameters **title** `string` *required* (default: 0) Title of the User Profile to delete. Must be present if `profileKey` is not passed. `title` is case-sensitive and must match the User Profile title. ```javascript cURL theme={"system"} curl \ -H "Authorization: Bearer API_KEY" \ -H 'Content-Type: application/json' \ -H 'Profile-Key: PROFILE_KEY' \ -X DELETE https://api.ayrshare.com/api/profiles ``` ```javascript JavaScript theme={"system"} const API_KEY = "API_KEY"; const profileKey = "PROFILE_KEY"; fetch("https://api.ayrshare.com/api/profiles", { method: "DELETE", headers: { "Content-Type": "application/json", "Authorization": `Bearer ${API_KEY}`, "Profile-Key": profileKey } }) .then((res) => res.json()) .then((json) => console.log(json)) .catch(console.error); ``` ```python Python theme={"system"} import requests payload = {} headers = {'Content-Type': 'application/json', 'Authorization': 'Bearer API_KEY', 'Profile-Key': 'PROFILE_KEY'} r = requests.delete('https://api.ayrshare.com/api/profiles', json=payload, headers=headers) print(r.json()) ``` ```php PHP theme={"system"} $url, CURLOPT_RETURNTRANSFER => true, CURLOPT_CUSTOMREQUEST => 'DELETE', CURLOPT_HTTPHEADER => [ 'Content-Type: application/json', 'Authorization: Bearer ' . $apiKey, 'Profile-Key: ' . $profileKey ], ]); $response = curl_exec($curl); if (curl_errno($curl)) { echo 'Error:' . curl_error($curl); } else { echo json_encode(json_decode($response), JSON_PRETTY_PRINT); } curl_close($curl); ``` ```csharp C# theme={"system"} using System; using System.Net.Http; using System.Threading.Tasks; class Program { static async Task Main(string[] args) { using var client = new HttpClient(); var requestUri = "https://api.ayrshare.com/api/profiles"; var bearerToken = "Bearer API_KEY"; var profileKey = "PROFILE_KEY"; using var request = new HttpRequestMessage(HttpMethod.Delete, requestUri); request.Headers.Add("Authorization", bearerToken); request.Headers.Add("Profile-Key", profileKey); try { using var response = await client.SendAsync(request); response.EnsureSuccessStatusCode(); var responseBody = await response.Content.ReadAsStringAsync(); Console.WriteLine(responseBody); } catch (HttpRequestException e) { Console.WriteLine($"Error: {e.Message}"); } } } ``` ```javascript 200: Success theme={"system"} { "status": "success", "refId": "823nd82nd92jsnn2932" } ``` ```javascript 500: Error Deleting theme={"system"} { "action": "delete", "status": "error", "code": 147, "message": "Error deleting profile." } ``` ```javascript 403: Profile Key Not Found theme={"system"} { "action": "post", "status": "error", "code": 144, "message": "Some profiles not found. Please verify the Profile Keys." } ``` # Generate a Linking URL (generateJWT) Source: https://www.ayrshare.com/docs/apis/profiles/generate-jwt POST /profiles/generateJWT Create a social-linking URL for a User Profile with the generateJWT endpoint. Create a social-linking URL for a User Profile. The endpoint is still named `generateJWT`, but the token it returns is opaque and is not a JWT. See the [Social Linking Overview](/docs/apis/profiles/social-linking-overview) for more details. > [!NOTE] > > The linking URL is valid for **5 minutes**. After 5 minutes you must generate a new linking URL. > See the Max Pack `expiresIn` for [additional options](/docs/apis/profiles/social-linking-overview#jwt-expires-in). > [!NOTE] > > **This endpoint is deprecated.** It is fully supported, has no removal date, and keeps working > unchanged — it is simply no longer the recommended way to create a linking URL. > > The returned `token` is an **opaque string** beginning `ayr_ls_` — it is not a > decodable JWT and carries no readable payload. Pass the `url` to your user as > returned, and treat `token` as a credential you store rather than inspect. > Links you created earlier still work. > > `POST /profiles/link-sessions` is the same operation without the legacy > parameters, and it also lets you check or revoke a link. See > [Create a Link Session](/docs/apis/profiles/create-link-session). ## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` **X-Twitter-OAuth1-Api-Key** `string` Your X API Key (Consumer Key) from the X Developer Portal. Alternative to the `twitterApiKey` body parameter. When provided, the generated linking URL will use your X Developer App for OAuth linking. **X-Twitter-OAuth1-Api-Secret** `string` Your X API Secret (Consumer Secret) from the X Developer Portal. Alternative to the `twitterApiSecret` body parameter. Required when `X-Twitter-OAuth1-Api-Key` is provided. > [!NOTE] > > **Recommended:** Pass your X credentials via headers (`X-Twitter-OAuth1-Api-Key` and `X-Twitter-OAuth1-Api-Secret`) for consistency with all other Ayrshare API endpoints. The `twitterApiKey` and `twitterApiSecret` body parameters are still supported for backward compatibility. ## Body Parameters **domain** `string` Domain of app. Please use the exact domain given during onboarding. Optional. When omitted, your account's own domain is used. If you do send it, it must be a domain registered to your account. **privateKey** `string` *deprecated* No longer used. Accepted and ignored, so existing integrations keep working without a change. Linking URLs are no longer signed, so there is no key to send. You can remove this field, and stop storing the private key for this purpose. If your integration also signs its own tokens, keep the private key. See [Self-Signed Tokens (Legacy)](/docs/apis/profiles/social-linking-overview#self-signed-tokens). **profileKey** `string` *required* User Profile Key. The API Key cannot be used in this field. **logout** `boolean` (default: false) Automatically logout the current session. Recommend not to use in production since it affects the performance. See [Automatic Logout of a Profile Session](/docs/multiple-users/api-integration-business#automatic-logout-of-a-profile-session) for more information. **redirect** `string` Specify a URL to redirect to when the "Done" button or logo image is clicked. The URL will be automatically shortened in the returned linking URL. [Redirect the origin ](/docs/multiple-users/api-integration-business#opening-&-closing-the-social-linking-url)opener window by adding the query parameter `origin=true` to the redirect URL. **allowedSocial** `array` Specify the social networks to display in the linking page. This will override the social networks configured in the [Social Networks](/docs/multiple-users/manage-user-profiles#set-social-networks-access) page. ```json Only display Facebook, X/Twitter, LinkedIn, and TikTok theme={"system"} { "allowedSocial": ["facebook", "twitter", "linkedin", "tiktok"] } ``` **instagramLinkMethod** `string` Override which Instagram linking flow is used when a user clicks the Instagram button on the social linking page for this URL. Valid values: * `instagram`: Direct Instagram Login, no Facebook Page required. * `facebook`: Link Instagram via a connected Facebook Page. ```json Force direct Instagram Login for this linking session theme={"system"} { "instagramLinkMethod": "instagram" } ``` When omitted, the linking page uses your account-wide [Instagram Login](/docs/multiple-users/manage-user-profiles#instagram-login) setting. See [Instagram Link Method](/docs/apis/profiles/social-linking-overview#instagram-link-method) for more information. **verify** `boolean` *deprecated* No longer used. Accepted and ignored. There is no signed token to verify — the returned `token` is validated by the linking page when your user opens the URL. **base64** `boolean` *deprecated* No longer used. Accepted and ignored, since `privateKey` is no longer read. **expiresIn** `number` (default: 5) Requires the Max Pack. Otherwise returns error `340` (HTTP 401). Set the longevity of the token in minutes. Range: 1 minute to 2880 minutes. See [Link Expiry](/docs/apis/profiles/social-linking-overview#jwt-expires-in) for more information. **email** `object` Requires the Max Pack. Otherwise returns error `340` (HTTP 401). Send a Connect Accounts email with a link for users to directly access their social linkage page. If the email can't be sent, you still get the link. The response has `emailSent: false` and an `emailError` object, so you can send the link to your user another way. See [Connect Accounts Email](/docs/apis/profiles/social-linking-overview#connect-accounts-email) for more information. > [!NOTE] > > When you include your X API credentials, the generated linking URL will initiate OAuth linking using your own X Developer App. Your end-users will see your app name on the X consent screen. > [!WARNING] > > **Required:** Before using this feature, you must add these callback URLs to your X Developer App settings (under Authentication settings > Callback URI / Redirect URL): > > * `https://profile.ayrshare.com/social-accounts` > * `https://app.ayrshare.com/social-accounts` > > Without these, the OAuth flow will fail with a `403 Callback URL not approved` error. ```javascript cURL theme={"system"} curl \ -H "Authorization: Bearer API_KEY" \ -H 'Content-Type: application/json' \ -H "X-Twitter-OAuth1-Api-Key: YOUR_CONSUMER_KEY" \ -H "X-Twitter-OAuth1-Api-Secret: YOUR_CONSUMER_SECRET" \ -d '{"profileKey": "PROFILE_KEY"}' \ -X POST https://api.ayrshare.com/api/profiles/generateJWT ``` ```javascript JavaScript theme={"system"} const API_KEY = "API_KEY"; const PROFILE_KEY = "PROFILE_KEY"; fetch("https://api.ayrshare.com/api/profiles/generateJWT", { method: "POST", headers: { "Content-Type": "application/json", "Authorization": `Bearer ${API_KEY}`, "X-Twitter-OAuth1-Api-Key": "YOUR_CONSUMER_KEY", "X-Twitter-OAuth1-Api-Secret": "YOUR_CONSUMER_SECRET" }, body: JSON.stringify({ profileKey: PROFILE_KEY, // required }), }) .then((res) => res.json()) .then((json) => console.log(json)) .catch(console.error); ``` ```python Python theme={"system"} import requests payload = {'profileKey': 'PROFILE_KEY'} headers = {'Content-Type': 'application/json', 'Authorization': 'Bearer API_KEY', 'X-Twitter-OAuth1-Api-Key': 'YOUR_CONSUMER_KEY', 'X-Twitter-OAuth1-Api-Secret': 'YOUR_CONSUMER_SECRET'} r = requests.post('https://api.ayrshare.com/api/profiles/generateJWT', json=payload, headers=headers) print(r.json()) ``` ```php PHP theme={"system"} request( 'POST', 'https://api.ayrshare.com/api/profiles/generateJWT', [ 'headers' => [ 'Content-Type' => 'application/json', 'Authorization' => 'Bearer API_KEY' ], 'json' => [ 'profileKey' => 'PROFILE_KEY', // required ] ] ); echo json_encode(json_decode($res->getBody()), JSON_PRETTY_PRINT); ``` ```csharp C# theme={"system"} using System; using System.Net.Http; using System.Text; using System.Threading.Tasks; using Newtonsoft.Json; namespace GenerateJWTRequest_csharp { class GenerateJWT { static async Task Main(string[] args) { string API_KEY = "API_KEY"; string url = "https://api.ayrshare.com/api/profiles/generateJWT"; try { var sendData = new { profileKey = "PROFILE_KEY" }; using (var client = new HttpClient()) { client.DefaultRequestHeaders.Add("Authorization", "Bearer " + API_KEY); string jsonData = JsonConvert.SerializeObject(sendData); var content = new StringContent(jsonData, Encoding.UTF8, "application/json"); HttpResponseMessage response = await client.PostAsync(url, content); response.EnsureSuccessStatusCode(); string responseBody = await response.Content.ReadAsStringAsync(); Console.WriteLine(responseBody); } } catch (HttpRequestException e) { Console.WriteLine($"HTTP request error: {e.Message}"); } catch (Exception e) { Console.WriteLine($"Unexpected error: {e.Message}"); } } } } ``` ```json 200: Success theme={"system"} { "status": "success", "title": "User Profile Title", "token": "ayr_ls_kJ8mQ2vX9pLnR4tYwZ6aBcD1eFgH3iJkLmN0oPqRsTu", "url": "https://profile.ayrshare.com?session=ayr_ls_kJ8mQ2vX9pLnR4tYwZ6aBcD1eFgH3iJkLmN0oPqRsTu&domain=PROVIDED_DOMAIN", "emailSent": true, "expiresIn": "5m" } ``` ```json 200: Email Failed to Send theme={"system"} { "status": "success", "title": "User Profile Title", "token": "ayr_ls_kJ8mQ2vX9pLnR4tYwZ6aBcD1eFgH3iJkLmN0oPqRsTu", "url": "https://profile.ayrshare.com?session=ayr_ls_kJ8mQ2vX9pLnR4tYwZ6aBcD1eFgH3iJkLmN0oPqRsTu&domain=PROVIDED_DOMAIN", "emailSent": false, "emailError": { "code": 333, "message": "Email failed to send. Please confirm a valid email was provided." }, "expiresIn": "5m" } ``` ```json 400: Domain Not Registered to Your Account theme={"system"} { "action": "JWT", "status": "error", "code": 189, "message": "Error generating JWT. Check the sent parameters.", "details": "Missing or incorrect domain." } ``` ```json 400: Partial Twitter API Keys theme={"system"} { "action": "JWT", "status": "error", "code": 188, "message": "Both twitterApiKey and twitterApiSecret are required. You provided only one." } ``` ```json 400: Duplicate X/Twitter Credentials theme={"system"} { "action": "JWT", "status": "error", "code": 434, "message": "X/Twitter API credentials were provided in both the request headers and the request body. Please use the headers only. See https://www.ayrshare.com/docs/apis/profiles/generate-jwt" } ``` # Get a Link Session Source: https://www.ayrshare.com/docs/apis/profiles/get-link-session GET /profiles/link-sessions/{sessionId} Check whether a social-linking URL has been opened, is still valid, or has been revoked. Check the state of a linking URL you created with [Create a Link Session](/docs/apis/profiles/create-link-session) — whether your user has opened it, whether it is still valid, and how many times it has been used. Useful when you want to know if a user has started connecting their accounts without asking them, or to confirm a link is dead before creating a replacement. > [!NOTE] > > A `sessionId` that does not exist, or that belongs to another account, returns the same > `not found` response. This is deliberate, so the endpoint cannot be used to discover > whether an identifier is real. ## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` **Profile-Key** `string` *required* [Profile Key](/docs/apis/overview#profile-key-format) of a User Profile. Format: `Profile-Key: PROFILE_KEY` ## Path Parameters **sessionId** `string` *required* The `sessionId` returned when the link was created. ## Link States The `state` field is one of: * `pending`: created, never opened. * `active`: opened at least once, still within its window. * `expired`: past `expiresAt`. * `revoked`: killed with [Revoke a Link Session](/docs/apis/profiles/revoke-link-session). A revoked link reports `revoked` even after its window passes, since that is the more useful answer. ## Connected Accounts When your user connects an account, the link records it. Three fields appear once something has been connected, and are absent until then: * `completedAt`: when the first account was connected. * `lastCompletedAt`: when the most recent one was. * `completedNetworks`: every network connected on this link. These are separate from `state`, and both answer real questions: a link can be `active` and have connected nothing yet, or `expired` and have connected three accounts before it ran out. Polling for `completedNetworks` is how a server-rendered app or a mobile app knows a connection finished, and it is the only signal for Telegram, which completes out of band. > [!WARNING] > > **`completedNetworks` is a history of this link session, not the profile's current > state.** It is append-only: a network is added when it connects through this session and > is never removed, so it stays listed after the account is unlinked, relinked through a > different session, or disconnected on the network's side. To see which networks are > linked to the User Profile **right now**, call > [Get User Profiles](/docs/apis/profiles/get-profiles) with `include=state`. ```bash cURL theme={"system"} curl \ -H "Authorization: Bearer API_KEY" \ -H 'Profile-Key: PROFILE_KEY' \ -X GET https://api.ayrshare.com/api/profiles/link-sessions/SESSION_ID ``` ```javascript JavaScript theme={"system"} const API_KEY = "API_KEY"; const PROFILE_KEY = "PROFILE_KEY"; const SESSION_ID = "SESSION_ID"; fetch(`https://api.ayrshare.com/api/profiles/link-sessions/${SESSION_ID}`, { headers: { Authorization: `Bearer ${API_KEY}`, "Profile-Key": PROFILE_KEY, }, }) .then((res) => res.json()) .then((json) => console.log(json)) .catch(console.error); ``` ```python Python theme={"system"} import requests headers = {'Authorization': 'Bearer API_KEY', 'Profile-Key': 'PROFILE_KEY'} response = requests.get( 'https://api.ayrshare.com/api/profiles/link-sessions/SESSION_ID', headers=headers) print(response.json()) ``` ```php PHP theme={"system"} request( 'GET', 'https://api.ayrshare.com/api/profiles/link-sessions/SESSION_ID', [ 'headers' => [ 'Authorization' => 'Bearer API_KEY', 'Profile-Key' => 'PROFILE_KEY' ] ] ); echo json_encode(json_decode($res->getBody()), JSON_PRETTY_PRINT); ``` ```csharp C# theme={"system"} using System; using System.Net.Http; using System.Threading.Tasks; namespace GetLinkSession_csharp { class GetLinkSession { static async Task Main(string[] args) { string API_KEY = "API_KEY"; string PROFILE_KEY = "PROFILE_KEY"; string url = "https://api.ayrshare.com/api/profiles/link-sessions/SESSION_ID"; try { using (var client = new HttpClient()) { client.DefaultRequestHeaders.Add("Authorization", "Bearer " + API_KEY); client.DefaultRequestHeaders.Add("Profile-Key", PROFILE_KEY); HttpResponseMessage response = await client.GetAsync(url); response.EnsureSuccessStatusCode(); string responseBody = await response.Content.ReadAsStringAsync(); Console.WriteLine(responseBody); } } catch (HttpRequestException e) { Console.WriteLine($"HTTP request error: {e.Message}"); } } } } ``` ```json 200: Opened Once, Still Valid theme={"system"} { "status": "success", "sessionId": "c7a2434e72e91bde27579efde0fd6dd0b74ceee29a471fd368407f273708c2e8", "state": "active", // pending | active | expired | revoked - see Link States above. "createdAt": "2026-09-01T08:03:26.838Z", // When the link was created, as an ISO 8601 timestamp. "expiresAt": "2026-09-02T08:03:26.838Z", // When the link stops working. "firstUsedAt": "2026-09-01T08:14:02.104Z", // When the link was first opened. Absent if it never has been. "lastUsedAt": "2026-09-01T08:14:02.104Z", // When the link was most recently opened. Absent if it never has been. "useCount": 1, // How many times the link has been opened. A reload or an OAuth retry increments this. "completedAt": "2026-09-01T08:15:44.870Z", // When the first account was connected. Absent until one is. "lastCompletedAt": "2026-09-01T08:16:20.412Z", // When the most recent was connected. Absent until one is. "completedNetworks": ["reddit", "bluesky"] // Every network connected on this link, append-only. Absent until one is. } ``` ```json 200: Created, Never Opened theme={"system"} { "status": "success", "sessionId": "c7a2434e72e91bde27579efde0fd6dd0b74ceee29a471fd368407f273708c2e8", "state": "pending", // Created but never opened, so firstUsedAt and lastUsedAt are absent. "createdAt": "2026-09-01T08:03:26.838Z", "expiresAt": "2026-09-02T08:03:26.838Z", "useCount": 0 } ``` ```json 404: Not Found theme={"system"} { "action": "link session", "status": "error", "code": 502, "message": "Social linking session not found. Please request a new linking URL." } ``` # Get User Profiles Source: https://www.ayrshare.com/docs/apis/profiles/get-profiles GET /profiles Get the User Profiles under your Primary Profile, filtered by title, refId, social accounts, or BYOK status, or get the profile create and delete log. Get all the profiles associated with the primary profile. The Primary Profile is not returned in the results. For security, the Profile Keys are not returned via this GET call. Please see [here](/docs/apis/profiles/create-profile) for more information. ## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` ## Query Parameters **title** `string` Return only the profile associated with the URL encoded title. **refId** `string` Return only the profile associated with the given `refId`. The refId was returned during the profile creation or from the /user endpoint. **hasActiveSocialAccounts** `boolean` (default: false) If `true` return only profiles that have at least one connected social account (`activeSocialAccounts` length greater than zero). If `false` return only profiles with zero connected social accounts (`activeSocialAccounts` length is zero). **includesActiveSocialAccounts** `array` Filter profiles to include those whose `activeSocialAccounts` contain all of the social media platforms specified in the `includesActiveSocialAccounts` list. Profiles can have additional platforms in their `activeSocialAccounts` beyond those listed in `includesActiveSocialAccounts` and still be included in the filtered results. Values: `bluesky`, `facebook`, `gmb`, `instagram`, `linkedin`, `pinterest`, `reddit`, `snapchat`, `telegram`, `threads`, `tiktok`, `twitter`, `youtube`. **isByokLinked** `boolean` Filter profiles by BYOK (Bring Your Own Key) migration status. If `true`, return only profiles that have completed BYOK migration. If `false`, return only profiles that have a BYOK-eligible platform connected but have not yet migrated. Profiles without a BYOK-eligible platform are excluded when this filter is set. When omitted, all profiles are returned regardless of BYOK status. Currently applies to [X/Twitter BYOK](/docs/dashboard/connect-social-accounts/x-twitter-byo-keys). **actionLog** `boolean` (default: false) Return the User Profile create and delete action log history for the past 60 days and the daily User Profile count for the past 60 days. Note:
    • Action Log: Title and tags will not be returned for action log history prior to March 2025.
    • User Profile Report: The time period might not correspond to your billing period. For example, your billing period might start and end on the 10th of the month. Please see your invoices for the billing period and other details.
    You may specify a different time period by setting the actionLog to a number of days. For example, `actionLog=10` query parameter will return the action log for the past 10 days and the daily User Profile count for the past 10 days. The time period allowed is 1 day to 365 days. **limit** `number` (default: 5000) Limit the number of profiles returned. The default and maximum is 5000. **cursor** `string` If there are additional profiles to return and the `hasMore` flag is `true`, the `nextCursor` will be returned in the response. Pass this cursor to the `cursor` query parameter to return the next set of profiles. **include** `string | array` Return extended profile data for troubleshooting. Requires `refId` parameter (single profile lookup). Values (comma-separated string or array): `suspension`, `socialHealth`, `linkingErrors`, `activity`, `quota`, `unlinkHistory`, `actionLog`, `state`.
    • `suspension` - isSuspended, reason, suspendedAt, unsuspendAt, suspensionCount
    • `socialHealth` - Per-platform: linked, linkedAt, relinkRecommended, messagingEnabled, tokenExpiresAt
    • `linkingErrors` - Per-platform: code, message, details, createdAt
    • `activity` - lastApiCall, lastPost timestamps
    • `quota` - used, limit
    • `unlinkHistory` - Last unlink: platform, source (user/system), details, createdAt
    • `actionLog` - Profile action history (create/update/delete)
    • `state` - Per-network: a single normalized `state` value, plus `since`, `actionable`, `message`, and `helpUrl`
    Every social network is always present in the `state` block, so a network that has never been linked reports `unlinked` rather than being omitted. Exactly one `state` value is returned per network, resolved in this order when more than one condition applies: | `state` | Meaning | `actionable` | | - | - | - | | `suspended` | The account is suspended, so the network cannot be used or linked. | `false` | | `unlinked` | The network is not linked to this profile. | `true` | | `identityVerificationRequired` | Meta requires the account holder to re-verify their identity. Posting returns `code: 326`. | `true` | | `restricted` | The access token is no longer valid. Relink to restore access. | `true` | | `rateLimited` | The account exceeded its API rate limit. Resets automatically at the end of the day. | `false` | | `linked` | Linked and healthy. | `false` | `actionable` tells you whether the end user can act to clear the state, so it is what decides whether your row shows a button. `since` is included only where a timestamp is recorded for that state, and is omitted otherwise - notably for `restricted`, which records no date. ```javascript cURL theme={"system"} curl \ -H "Authorization: Bearer API_KEY" \ -X GET https://api.ayrshare.com/api/profiles ``` ```javascript cURL (with include) theme={"system"} curl \ -H "Authorization: Bearer API_KEY" \ -X GET "https://api.ayrshare.com/api/profiles?refId=160c8700bd6ade&include=suspension,socialHealth,activity,quota" ``` ```javascript JavaScript theme={"system"} const API_KEY = "API_KEY"; fetch("https://api.ayrshare.com/api/profiles", { method: "GET", headers: { "Authorization": `Bearer ${API_KEY}` } }) .then((res) => res.json()) .then((json) => console.log(json)) .catch(console.error); ``` ```python Python theme={"system"} import requests headers = {'Authorization': 'Bearer API_KEY'} r = requests.get('https://api.ayrshare.com/api/profiles', headers=headers) print(r.json()) ``` ```php PHP theme={"system"} $url, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPGET => true, CURLOPT_HTTPHEADER => [ 'Content-Type: application/json', 'Authorization: Bearer ' . $apiKey ], ]); $response = curl_exec($curl); if (curl_errno($curl)) { echo 'Error:' . curl_error($curl); } else { echo json_encode(json_decode($response), JSON_PRETTY_PRINT); } curl_close($curl); ``` ```csharp C# theme={"system"} using System; using System.Net.Http; using System.Threading.Tasks; namespace ProfilesGETRequest_csharp { class Profiles { static async Task Main(string[] args) { string API_KEY = "API_KEY"; string url = "https://api.ayrshare.com/api/profiles"; using (var client = new HttpClient()) { client.DefaultRequestHeaders.Add("Authorization", "Bearer " + API_KEY); try { HttpResponseMessage response = await client.GetAsync(url); response.EnsureSuccessStatusCode(); string responseBody = await response.Content.ReadAsStringAsync(); Console.WriteLine(responseBody); } catch (HttpRequestException e) { Console.WriteLine($"Error: {e.Message}"); } } } } } ``` ```javascript 200: Success theme={"system"} { "profiles": [ { "status": "active", "title": "Digg It Title", "displayTitle": "Your title", "created": { "_seconds": 1604094099, "_nanoseconds": 530000000 }, "createdUTC": "2022-03-02T16:11:00.839Z", "refId": "160c8700bd6ade099b242d845e268fb986130c53", "activeSocialAccounts": [ "twitter", "facebook", "linkedin", "instagram" ], "isByokLinked": true }, { "status": "active", "title": "Super Profile", "created": { "_seconds": 1604377627, "_nanoseconds": 252000000 }, "createdUTC": "2022-03-02T16:11:00.839Z", "refId": "170a8700bd6ade099b242d845e268fb986130c53" }, { "status": "suspended", "title": "Good Fun Title", "created": { "_seconds": 1605107864, "_nanoseconds": 96000000 }, "createdUTC": "2022-03-02T16:11:00.839Z", "refId": "180s8700bd6ade099b242d845e268fb986130c53", "activeSocialAccounts": [ "facebook", "linkedin", "youtube" ], "suspended": true } ], "count": 100, "lastUpdated": "2025-06-14T15:53:26.016Z", "nextUpdate": "2025-06-14T15:58:26.016Z", "pagination": { "hasMore": true, "nextCursor": "eyJjcmVhdGVkIjoiMjAyNS0wNi0xNFQwMjo1", "limit": 100 } } ``` ```json 200: Action Log theme={"system"} { "profiles": { "actionLog": [ { "action": "create", "refId": "2d83hd839282ehd892d2999912d1dsdgldfkepw", "title": "Profile 1", "created": "2025-05-29T14:10:33.709Z" }, { "action": "create", "refId": "fmm02nd9c3nm9djjffdfsfshfihvp848jcsf222s", "title": "Profile 2", "created": "2025-05-28T20:33:32.186Z" } ], "userProfileReport": [ { "reported": "2025-04-01T08:00:16.632Z", "userProfilesCount": 135 }, { "reported": "2025-03-31T08:00:22.651Z", "userProfilesCount": 135 } ] }, "lastUpdated": "2025-06-14T15:56:02.260Z", "nextUpdate": "2025-06-14T16:01:02.260Z" } ``` ```json 200: Include Extended Data theme={"system"} { "profiles": [ { "title": "Brand Marketing", "refId": "160c8700bd6ade099b242d845e268fb986130c53", "status": "suspended", "activeSocialAccounts": ["twitter", "instagram"], "suspension": { "isSuspended": true, "reason": "Too many duplicate posts", "suspendedAt": "2026-03-01T10:00:00.000Z", "unsuspendAt": "2026-03-03T10:00:00.000Z", "suspensionCount": 2 }, "socialHealth": { "twitter": { "linked": true, "linkedAt": "2026-01-15T10:30:00.000Z", "relinkRecommended": false, "messagingEnabled": true, "tokenExpiresAt": null }, "instagram": { "linked": true, "linkedAt": "2026-02-01T08:00:00.000Z", "relinkRecommended": true, "messagingEnabled": false, "tokenExpiresAt": "2026-04-01T08:00:00.000Z" } }, "activity": { "lastApiCall": "2026-03-05T09:15:00.000Z", "lastPost": "2026-03-04T14:30:00.000Z" }, "quota": { "used": 450, "limit": 1000 } } ], "count": 1, "pagination": { "hasMore": false, "nextCursor": null, "limit": 5000 } } ``` # Profiles API Overview Source: https://www.ayrshare.com/docs/apis/profiles/overview Create and manage User Profiles, call endpoints on their behalf with the Profile Key, enable or disable social networks, and use Demo User Profiles. > [!NOTE] > > Specify a User Profile in an API call by [adding the Profile Key in the header](/docs/apis/overview#profile-key-format). User Profiles are one of the core concepts of Ayrshare that allows you to create and manage multiple users. Each one of the users of your platform will have one or more Ayrshare User Profiles. Each User Profile can have one connection to every supported social network. ## Profile Key Many endpoints, such as /user, /analytics, or /delete, can be called on behalf of a Profile account by [adding the "Profile-Key" parameter](/docs/apis/overview#profile-key-format) in the Header. For example, the /delete endpoint can be called to delete a Profile account's post for the given *post* *id* for the provided *Profile Key*. ```javascript theme={"system"} const API_KEY = "API_KEY"; const PROFILE_KEY = "PROFILE_KEY"; const id = "Post ID"; fetch("https://api.ayrshare.com/api/delete", { method: "DELETE", headers: { "Content-Type": "application/json", "Authorization": `Bearer ${API_KEY}` "Profile-Key": PROFILE_KEY }, body: JSON.stringify({ id: id }), }) .then((res) => res.json()) .then((json) => console.log(json)) .catch(console.error); ``` ## Enable or Disable Social Networks You can disable and enable social networks at either the global level or at the User Profile level.
    • [Globally manage social account access](/docs/multiple-users/manage-user-profiles#set-social-networks-access) in the dashboard.
    • Manage social account access for a User Profile when [creating](/docs/apis/profiles/create-profile) or [updating](/docs/apis/profiles/update-profile).
    It is important to note that disabled social networks in the global setting, set via the dashboard, override User Profile settings. For example, if you disable TikTok access in the dashboard, then no User Profile can access TikTok. On the other hand, if you turn on TikTok access in the dashboard but disable it for a specific User Profile, all User Profiles will have access to TikTok except that particular one. ## Get the Linked Social Accounts Retrieve the social accounts a User Profile has linked with either the [/profiles endpoint](/docs/apis/profiles/get-profiles) or the [/user endpoint](/docs/apis/user/profile-details). ## Post to a User Profile With the [/post](/docs/apis/post/post) endpoint, you can post to a User Profile by adding the Profile Key in the header. The return will be an array of posts or error results, with an overall status. If all posts are successful, "success", or if any post failed, "error". You may obtain a user's Profile Key when the profile is created with the [/create-profile](/docs/apis/profiles/create-profile) endpoint or in Ayrshare's Web Dashboard GUI by switching to the profile and going to **Profile Key** page. ## Demo User Profiles Demo User Profiles let you offer a prospective end user a 30-day evaluation of your platform's social features before committing them to a standard User Profile. Create one by setting `demo: true` when [creating a profile](/docs/apis/profiles/create-profile). A Demo User Profile works just like a standard User Profile — your user can link social accounts, post, and retrieve analytics using its Profile Key. After 30 days, the Demo User Profile automatically converts to a standard User Profile. If you have a `demo` [webhook](/docs/apis/webhooks/overview) registered, you'll receive an `upgradeWarning` event two days before the conversion and an `upgrade` event when the conversion happens, so you can prompt your user (or remove the profile) beforehand. > [!NOTE] > > Demo User Profiles are available on the Enterprise plan and must be enabled for your account. Please [contact us](mailto:support@ayrshare.com) if you'd like to learn more. # Revoke a Link Session Source: https://www.ayrshare.com/docs/apis/profiles/revoke-link-session DELETE /profiles/link-sessions/{sessionId} Kill a social-linking URL before it expires so it can no longer be used. Kill a linking URL before it expires. Once revoked, opening it does nothing — your user sees an expired-link page instead of their profile. Use this when a link went to the wrong person, was sent over a channel you no longer trust, or is simply no longer wanted. Revoking is the reason a link session is worth creating over the older signed URLs, which could not be recalled once sent. > [!NOTE] > > Revoking is idempotent. Revoking an already-revoked link succeeds and returns the same > response, so a retry is always safe. > [!NOTE] > > Revoking also removes any X/Twitter credentials you supplied for this link. Accounts > your user already connected through the link stay connected — revoking kills the link, > not the social accounts. ## Header Parameters **Authorization** `string` *required* [API Key](/docs/apis/overview#authorization) of the Primary Profile. Format: `Authorization: Bearer API_KEY` **Profile-Key** `string` *required* [Profile Key](/docs/apis/overview#profile-key-format) of a User Profile. Format: `Profile-Key: PROFILE_KEY` ## Path Parameters **sessionId** `string` *required* The `sessionId` returned when the link was created. ```bash cURL theme={"system"} curl \ -H "Authorization: Bearer API_KEY" \ -H 'Profile-Key: PROFILE_KEY' \ -X DELETE https://api.ayrshare.com/api/profiles/link-sessions/SESSION_ID ``` ```javascript JavaScript theme={"system"} const API_KEY = "API_KEY"; const PROFILE_KEY = "PROFILE_KEY"; const SESSION_ID = "SESSION_ID"; fetch(`https://api.ayrshare.com/api/profiles/link-sessions/${SESSION_ID}`, { method: "DELETE", headers: { Authorization: `Bearer ${API_KEY}`, "Profile-Key": PROFILE_KEY, }, }) .then((res) => res.json()) .then((json) => console.log(json)) .catch(console.error); ``` ```python Python theme={"system"} import requests headers = {'Authorization': 'Bearer API_KEY', 'Profile-Key': 'PROFILE_KEY'} response = requests.delete( 'https://api.ayrshare.com/api/profiles/link-sessions/SESSION_ID', headers=headers) print(response.json()) ``` ```php PHP theme={"system"} request( 'DELETE', 'https://api.ayrshare.com/api/profiles/link-sessions/SESSION_ID', [ 'headers' => [ 'Authorization' => 'Bearer API_KEY', 'Profile-Key' => 'PROFILE_KEY' ] ] ); echo json_encode(json_decode($res->getBody()), JSON_PRETTY_PRINT); ``` ```csharp C# theme={"system"} using System; using System.Net.Http; using System.Threading.Tasks; namespace RevokeLinkSession_csharp { class RevokeLinkSession { static async Task Main(string[] args) { string API_KEY = "API_KEY"; string PROFILE_KEY = "PROFILE_KEY"; string url = "https://api.ayrshare.com/api/profiles/link-sessions/SESSION_ID"; try { using (var client = new HttpClient()) { client.DefaultRequestHeaders.Add("Authorization", "Bearer " + API_KEY); client.DefaultRequestHeaders.Add("Profile-Key", PROFILE_KEY); HttpResponseMessage response = await client.DeleteAsync(url); response.EnsureSuccessStatusCode(); string responseBody = await response.Content.ReadAsStringAsync(); Console.WriteLine(responseBody); } } catch (HttpRequestException e) { Console.WriteLine($"HTTP request error: {e.Message}"); } } } } ``` ```json 200 theme={"system"} { "status": "success", "sessionId": "c7a2434e72e91bde27579efde0fd6dd0b74ceee29a471fd368407f273708c2e8", "state": "revoked", // Always "revoked" on success. "revokedAt": "2026-09-01T09:22:11.507Z" // When the link was revoked. On a repeat revoke, the ORIGINAL time - not the retry. } ``` ```json 404: Not Found theme={"system"} { "action": "link session", "status": "error", "code": 502, "message": "Social linking session not found. Please request a new linking URL." } ``` # Social Linking Overview Source: https://www.ayrshare.com/docs/apis/profiles/social-linking-overview How your users connect their social accounts — the three surfaces, the link session behind all of them, and the options they share. Your users connect their own social accounts themselves — they authenticate directly with each network, and you never see or store their credentials. Everything on this page is about getting them to that moment and finding out how it went. One thing is common to every route: a **link session**, created with [Create a Link Session](/docs/apis/profiles/create-link-session) from your API key and a `Profile-Key`. Nothing is signed on your side and there is no private key in the flow. The one exception is older integrations that sign their own tokens. See [Self-Signed Tokens (Legacy)](#self-signed-tokens). ## Three Ways to Connect Three shapes, and the first two are the same integration. Pick per flow — they are not exclusive, and plenty of integrations use the hosted page for onboarding and the widget inside the app afterwards. | Surface | Your user sees | White-labelling | Choose it when | | - | - | - | - | | **Widget — embedded frames** ([mount](/docs/multiple-users/connect-widget#mount-a-slot)) | our buttons inline in your own layout; no popup until the network's own | **Strongest.** Your page, your fonts and colours, and your user never leaves it | you have a dashboard with a row per network and want linking to happen in place. Max Pack. | | **Widget — your own button** ([popup](/docs/multiple-users/connect-widget#your-own-button)) | your button, then one popup for the network | **Strong.** The popup is ours, but it is brief and inherits your appearance | you want your own button and styling, and no frames in your layout. Max Pack. | | **Hosted linking page** ([how to](/docs/multiple-users/api-integration-business#single-sign-on-with-jwt-authentication)) | a page we host, carrying your logo, colours and custom CSS | **Weakest.** It is our page, and your user leaves yours to use it | you want one link to hand out, or you are onboarding by email. Nothing to build, no Max Pack. | > [!NOTE] > > The two widget rows are **one integration**, not two. A single `init` gives you both: mount frames > where you want our buttons, and call `popup()` from your own button everywhere else. They share > one session and report on the same handlers. > > [Direct mode](/docs/multiple-users/connect-direct-mode) is the same popup **without** our script — for > a page with a strict Content-Security-Policy, a server-rendered page, or a native app. There you > open and watch the popup yourself. > > **Not sure?** Start with the hosted linking page. It needs no Max Pack and no extra parameters, > and it is the fastest route to something working — moving to the widget later does not change how > sessions are created. ## Creating a Link Call [Create a Link Session](/docs/apis/profiles/create-link-session) with the user's `Profile-Key` in the header. For the hosted page that is the whole request: ```bash cURL theme={"system"} curl -X POST https://api.ayrshare.com/api/profiles/link-sessions \ -H "Authorization: Bearer $AYRSHARE_API_KEY" \ -H "Profile-Key: $PROFILE_KEY" \ -H "Content-Type: application/json" \ -d '{}' ``` You get back a `url` carrying a short-lived, opaque token: ```javascript Linking URL theme={"system"} https://profile.ayrshare.com?session=ayr_ls_kJ8mQ2vX9pLnR4tYwZ6aBcD1eFgH3iJkLmN0oPqRsTu&domain=YOUR_DOMAIN ``` You can also [check whether a link has been opened](/docs/apis/profiles/get-link-session) and [revoke it](/docs/apis/profiles/revoke-link-session) before it expires. > [!NOTE] > > This one-minute video walks through creating a link. It predates link sessions, so it still shows > a Private Key being sent — that step is no longer needed, and everything else it shows is > unchanged. > > [Create a linking URL](https://www.youtube.com/embed/JI232HBWHWc) ### Sending the Linking URL > [!WARNING] > > A linking URL signs your user into their profile, so treat it like a password. Send it over a > channel you trust, don't log it, and don't pass it on to a third party. It stays usable for its > whole window, so a reload or an OAuth retry works — but send each link to one user only, and > create a separate link per person. ### Opening the Linking URL Open it in a new browser tab, window, or view controller. You may control the [closing or redirecting](/docs/multiple-users/api-integration-business#opening-&-closing-the-social-linking-url) of that window. > [!NOTE] > > The social networks do not allow the hosted linking page to be opened inside an iFrame, or the > approved partner origin `profile.ayrshare.com` to be obfuscated. If you want linking to happen > inside your own page, that is what the [embedded widget](/docs/multiple-users/connect-widget) is for: > its frames are served from an Ayrshare origin and are the supported way to do it. ### Knowing When It Is Done Two signals, and you can use either: * **[Link completion events](/docs/multiple-users/link-completion-events)** — set an `origin` when you create the link and the linking window posts `connect:success`, `connect:error` and `connect:cancelled` to your page as they happen. No polling. * **[Get a Link Session](/docs/apis/profiles/get-link-session)** — reports `completedAt`, `lastCompletedAt` and `completedNetworks`. This is the signal for native apps, and the only one for Telegram, which completes out of band. ## Link Expiry A link is valid for **5 minutes** by default. After that, create a new one. With the Max Pack, set `expiresIn` in minutes to widen that window — up to **2880 minutes (48 hours)**, which is the maximum the API accepts: ```json Expires In theme={"system"} { "expiresIn": 30 } ``` A longer window is what makes [emailing the link](#connect-accounts-email) practical — a user reconnecting a dropped account can go straight from your email to the network, without visiting your app first. > [!WARNING] > > Review with your security team how long a link should stay alive. A longer window is a longer > period in which an intercepted link still works. If one gets out, you can > [revoke it](/docs/apis/profiles/revoke-link-session) rather than waiting for it to expire. ## Security The session token travels in the linking URL's query string. Anything that records URLs can record it: the user's browser history, proxy and server logs, analytics tools, and redirect services. To limit the risk: * **Don't log the full URL** in your own systems. Log the profile and the time instead. * **Send the URL straight to the user.** Don't pass it through link shorteners, redirect trackers or email click-tracking. The [Connect Accounts Email](#connect-accounts-email) sends the link without click-tracking. * **Keep the window short.** Use the shortest [`expiresIn`](#jwt-expires-in) that works for you (Max Pack). * **Revoke a link** you think has leaked, using [Revoke a Link Session](/docs/apis/profiles/revoke-link-session). ## Profile Key The `Profile-Key` identifies which User Profile the link is for. Find it in the Ayrshare developer dashboard by switching to that profile. > [!NOTE] > > **You don't need the Private Key to create links.** Links from the API aren't signed, so there's > nothing to read from a file or paste into your code. The legacy `privateKey` parameter is still > accepted and ignored, so existing integrations keep working. > > If your integration signs its own tokens, keep your `private.key`. See > [Self-Signed Tokens (Legacy)](#self-signed-tokens). ## Switching Profiles If a profile is already signed in, opening another profile's link does not switch profiles — that is deliberate, and it keeps the experience fast for a user who is already there. To force a switch, see [Automatic Logout of a Profile Session](/docs/multiple-users/api-integration-business#automatic-logout-of-a-profile-session). ## Instagram Link Method Instagram accounts can be linked in [two ways](/docs/dashboard/connect-social-accounts/instagram): directly with **Instagram Login**, or via a **connected Facebook Page**. Which flow starts when a user clicks the Instagram button is normally controlled by the account-wide [Instagram Login](/docs/multiple-users/manage-user-profiles#instagram-login) setting. The `instagramLinkMethod` body parameter overrides that setting for a single link: | Value | Instagram Linking Flow | | - | - | | `instagram` | Direct Instagram Login. No Facebook Page required. | | `facebook` | Link via a connected Facebook Page. | ```json Instagram Link Method theme={"system"} { "instagramLinkMethod": "instagram" } ``` The override applies for the life of that link, including across the Instagram/Facebook authorization redirect. A few things to know: * It does not change your account-wide setting or affect any other link. * Omit it and the account-wide setting applies, exactly as before. * An invalid value returns `400` listing the valid values (`instagram`, `facebook`). * Review the [feature differences](/docs/multiple-users/manage-user-profiles#direct-instagram-login-vs-facebook-page-authentication) before choosing — some Instagram features, such as hashtag search and collaborations, are only available with Facebook Page authentication. ## Connect Accounts Email Ayrshare can email the link to your user for you, so they can reach their linking page without visiting your app. Pair it with a longer [`expiresIn`](#jwt-expires-in) — the default five minutes rarely survives an inbox. ### Connect Accounts JSON **Every field inside `email` is required.** A missing one fails the send. ```json Example Contact Email Request theme={"system"} { "expiresIn": 60, "email": { "to": "john@user.com", "contactEmail": "support@mycompany.com", "company": "ACME", "termsUrl": "https://www.ayrshare.com/terms", "privacyUrl": "https://www.ayrshare.com/privacy" } } ``` > [!WARNING] > > `expiresIn` is a **top-level** parameter, not part of the `email` object. Nested inside `email` it > is ignored, and your user gets a link that expires in five minutes. The response reports the outcome in `emailSent`: ```json Example Contact Email Response theme={"system"} { "status": "success", "sessionId": "c7a2434e72e91bde27579efde0fd6dd0b74ceee29a471fd368407f273708c2e8", "url": "https://profile.ayrshare.com?session=ayr_ls_kJ8mQ2vX9pLnR4tYwZ6aBcD1eFgH3iJkLmN0oPqRsTu&domain=YOUR_DOMAIN", "expiresAt": "2026-09-02T09:03:26.838Z", "emailSent": true, "title": "Acme Client" } ``` If the email can't be sent, the request still succeeds and you still get the link. The response has `emailSent: false` plus an `emailError` object, so you can send the link to your user another way: ```json Example Failed Email Response theme={"system"} { "status": "success", "sessionId": "c7a2434e72e91bde27579efde0fd6dd0b74ceee29a471fd368407f273708c2e8", "url": "https://profile.ayrshare.com?session=ayr_ls_kJ8mQ2vX9pLnR4tYwZ6aBcD1eFgH3iJkLmN0oPqRsTu&domain=YOUR_DOMAIN", "expiresAt": "2026-09-02T09:03:26.838Z", "emailSent": false, "emailError": { "code": 333, "message": "Email failed to send. Please confirm a valid email was provided." }, "title": "Acme Client" } ``` `emailError` appears whenever you asked for an email and it was not sent: either the send failed, or the `email` object was missing a required field, in which case `details` names the missing fields. `emailSent: false` with no `emailError` means no email was requested. A missing `to` is different: the whole request is rejected with `code: 188`. ### Connect Accounts Email Example Here is an example of the email that opens the social linking page: ![Connect Accounts email](https://mintcdn.com/ayrshare-docs/Nmrhj2Gh7WSf62Bh/images/apis/profiles/jwt-email.webp?fit=max\&auto=format\&n=Nmrhj2Gh7WSf62Bh\&q=85\&s=fbe4ee86ca59c26a5bd5b289fda96b8b) The email comes from: `Social Connect Hub ` ## Mobile Apps Open the linking URL in the **system browser**, never an embedded webview: Google rejects sign-in in one with `disallowed_useragent`, and Meta blocks it outright. Your user would see the network's own error page, and nothing on your side fixes it. * **iOS** — `ASWebAuthenticationSession`, or `SFSafariViewController`. * **Android** — Chrome Custom Tabs. Because a native app has no browser window to post events to, get the result from [Get a Link Session](/docs/apis/profiles/get-link-session) instead. Set `origin` to your custom scheme (`myapp://connected`) so the page has a way back into your app. ### Mobile Code Examples Replace `linkingURL` with the `url` returned from [Create a Link Session](/docs/apis/profiles/create-link-session). ```swift Swift theme={"system"} import UIKit import SafariServices class ViewController: UIViewController, SFSafariViewControllerDelegate { var linkingURL = "https://profile.ayrshare.com?session=ayr_ls_kJ8mQ2vX9pLnR4tYwZ6aBcD1eFgH3iJkLmN0oPqRsTu&domain=acme" override func viewDidLoad() { super.viewDidLoad() setupButton() } func setupButton() { let button = UIButton(type: .system) button.frame = CGRect(x: (view.bounds.width - 200) / 2, y: (view.bounds.height - 50) / 2, width: 200, height: 50) button.setTitle("Open URL", for: .normal) button.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside) view.addSubview(button) } @objc func buttonTapped() { openURLInInAppBrowser() } func openURLInInAppBrowser() { if let url = URL(string: linkingURL) { let safariVC = SFSafariViewController(url: url) safariVC.delegate = self present(safariVC, animated: true, completion: nil) } } // Optional: If you want to handle when the in-app browser is closed func safariViewControllerDidFinish(_ controller: SFSafariViewController) { controller.dismiss(animated: true, completion: nil) } } ``` ```dart Flutter theme={"system"} /** yaml dependencies dependencies: flutter: sdk: flutter url_launcher: ^6.2.1 */ import 'package:flutter/material.dart'; import 'package:url_launcher/url_launcher.dart'; void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'URL Launcher Example', theme: ThemeData( primarySwatch: Colors.blue, ), home: MyHomePage(), ); } } class MyHomePage extends StatelessWidget { final String linkingURL = "https://profile.ayrshare.com?session=ayr_ls_kJ8mQ2vX9pLnR4tYwZ6aBcD1eFgH3iJkLmN0oPqRsTu&domain=acme"; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('URL Launcher Example'), ), body: Center( child: ElevatedButton( onPressed: () { openURLInBrowser(context); }, child: Text('Open URL'), ), ), ); } void openURLInBrowser(BuildContext context) async { if (await canLaunch(linkingURL)) { await launch(linkingURL); } else { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('Could not launch $linkingURL'), ), ); } } } ``` ```jsx React Native theme={"system"} /** • Using the API provided by expo-web-browser, • which opens a URL in a modal browser window that shares cookies • with the system browser. • Learn more about expo: https://reactnative.dev/docs/environment-setup?guide=quickstart • and running the following command: • expo install expo-web-browser */ import React from 'react'; import { StyleSheet, Button, View } from 'react-native'; import * as WebBrowser from 'expo-web-browser'; export default function App() { const linkingURL = 'https://profile.ayrshare.com?session=ayr_ls_kJ8mQ2vX9pLnR4tYwZ6aBcD1eFgH3iJkLmN0oPqRsTu&domain=acme'; const openURLInBrowser = async () => { try { await WebBrowser.openBrowserAsync(linkingURL); // Optional: WebBrowser.openBrowserAsync returns a promise that resolves with an object containing // 'type' that can be 'cancelled' or 'dismissed'. You can use this to handle when the browser is closed. } catch (error) { console.error(error); } }; return ( ``` You may control the "Close" button to either redirect the social linking tab to a page of your choosing or redirect the origin opener tab/window and close the social linking tab. This can be done using the redirect parameter and adding `origin=true` to the query parameters of the redirect URL. See [Create a Link Session](/docs/apis/profiles/create-link-session) for more details. ### Redirecting When Closing Additionally, if you want to have the `Close` button redirect back to your page (often useful on mobile), pass in the `redirect` GET parameter as a *URL encoded* string starting with https. See the [Create a Link Session body parameters](/docs/apis/profiles/create-link-session#body-parameters) for more details. ```javascript Linking URL theme={"system"} https://profile.ayrshare.com?session=[session token]&domain=[domain id]&redirect=https%3A%2F%2Fmywebsite.com ``` The redirect will be saved for that profile and used in subsequent SSO calls even if the `redirect` parameter is not passed. To reset the `Close` button back to close, pass the parameter `redirect=null`. ## More Information ### User Linking or Unlinking Notifications When a user links or unlinks a social network, you can receive a notification via webhook. Please see the [Social Action Webhooks](/docs/apis/webhooks/actions#social-action) page for more information. You may also use the [user endpoint](/docs/apis/user/profile-details) to get the connected social networks for a user profile. ### Multiple Sets of Social Accounts #### Overview Each User Profile has a set of social media accounts and can make one connection to each social network. If your users or clients have multiple sets of social media accounts, such as two Facebook Pages, two Instagram accounts, and two YouTube channels, you can manage these multiple sets of social media accounts by following these steps: 1. **Create multiple User Profiles** - [Create separate User Profiles](/docs/apis/profiles/create-profile) within the Ayrshare system for each set of social accounts your user or client needs to manage. 2. **Store unique Profile Keys** - Each User Profile will have its own unique Profile Key, returned from the create User Profile endpoint. 3. **Label your profiles** - [Title](/docs/apis/profiles/create-profile#param-title) or [tag](/docs/apis/profiles/create-profile#param-tags) each User Profile to clearly identify which social media accounts they're associated with (e.g., "Facebook Page A" and "Facebook Page B"). 4. **Map in your database** - In your own system, associate all relevant Profile Keys with the respective user or client. 5. **Use appropriate Profile Key** - When posting content or retrieving analytics for a specific social account, use the Profile Key associated with that account. For example, to post to Facebook Page A, use the Profile Key of the User Profile connected to Facebook Page A. #### Example Let's consider a practical scenario for multiple client accounts:
    • **Client:** Joe's Restaurant Chain with two locations
    Joe needs separate social media accounts for each restaurant location: **Restaurant Location 1:**
    • Facebook Page: "Joe's Downtown Bistro"
    • Instagram: @joesdowntownbistro
    • LinkedIn: Joe's Downtown Bistro business page
    **Restaurant Location 2:**
    • Facebook Page: "Joe's Seaside Grill"
    • Instagram: @joesseasidegrill
    • LinkedIn: Joe's Seaside Grill business page
    **Implementation solution:** 1. Create two separate User Profiles in Ayrshare:
    • User Profile 1: Titled "Joe's Downtown Bistro Accounts"
    • User Profile 2: Titled "Joe's Seaside Grill Accounts"
    2. Connect the appropriate social accounts to each profile:
    • Profile 1: Links the Downtown Bistro Facebook, Instagram, and LinkedIn
    • Profile 2: Links the Seaside Grill Facebook, Instagram, and LinkedIn
    3. Store both Profile Keys in your database, associating them with Joe's account: ``` Client: Joe's Restaurant Chain - Profile Key 1: abc123... (Downtown location) - Profile Key 2: xyz789... (Seaside location) ``` 4. When posting a special menu update for the Downtown location, use Profile Key 1 in the API calls. When posting about a seafood promotion at the Seaside location, use Profile Key 2. By creating multiple User Profiles, [titling](/docs/apis/profiles/create-profile#param-title) or [tagging](/docs/apis/profiles/create-profile#param-tags) them appropriately, and associating them to a single user or client in your system, you can manage their multiple social media accounts across the same platforms using Ayrshare. This approach allows for targeted posting and analytics retrieval for each specific account. ### Next Steps Please see here for more details of the user experience: > [card] [User Integration](/docs/multiple-users/user-integration) Once your user sets up their social media links, you will be able to begin posting on their behalf using the /post and /profile endpoints. > [card] [Post API](/docs/apis/post/overview) > [card] [Profiles API](/docs/apis/profiles/overview) # Launch Plan: entry-level multi-user Business plan Source: https://www.ayrshare.com/docs/multiple-users/business-launch-overview The Ayrshare Launch Plan is the entry-level Business tier for agencies and platforms managing multi-user social media posting, analytics, and DMs. The **Launch Plan** is the entry-level tier of the [Ayrshare Business Plan](/docs/multiple-users/business-plan-overview). It's designed for companies, agencies, and platforms that are just starting to manage social media on behalf of their users, clients, or brands and don't yet need the scale of the full Business plan. The Launch Plan includes the same multi-user APIs as the full Business plan (posting, analytics, comments, DMs, and ads on behalf of your users) at a lower starting price, with a smaller profile cap to match the needs of early-stage teams. Post quotas, API rate limits, and support match the full Business plan. To get started, see current pricing and start your subscription on the [Ayrshare pricing page](https://www.ayrshare.com/pricing/). ## Who Launch Is For
    • Early-stage SaaS platforms adding social posting for their first users.
    • Agencies onboarding a small set of clients before scaling further.
    • Teams running a pilot or proof-of-concept that needs production-grade APIs without committing to the full Business plan.
    • Anyone who needs Business-tier features (multi-user JWT linking, per-user analytics, comments, DMs, ads) for **up to 10 user profiles**.
    > [!NOTE] > > The Launch Plan is capped at 10 [User Profiles](/docs/multiple-users/manage-user-profiles) > (sub-profiles, separate from your primary account). Once you reach the cap, > the API returns an error with upgrade guidance, and the dashboard shows the > same message as a toast. To add more, upgrade to the full > [Business Plan](/docs/multiple-users/business-plan-overview). ## What's Included The Launch Plan gives you the full Business API surface for managing multiple users, capped at 10 [User Profiles](/docs/multiple-users/manage-user-profiles):
    • A single API call to post to your users' [social media networks](/docs/apis/post/overview): Bluesky, Facebook, Google Business Profile, Instagram, LinkedIn, Pinterest, Reddit, Snapchat, Telegram, Threads, TikTok, X, and YouTube.
    • [Schedule posts](/docs/apis/post/overview#schedule-posts) and [auto-schedule](/docs/apis/auto-schedule/overview) at pre-determined times.
    • [Per-user analytics](/docs/apis/analytics/overview) on posts, social profiles, and links.
    • [Comment management](/docs/apis/comments/overview) and [direct messages](/docs/apis/messages/overview) on supported platforms.
    • [Facebook Ads](/docs/apis/ads/overview) created from existing posts.
    • [Hashtag automation](/docs/apis/hashtags/overview), [RSS feeds](/docs/apis/feeds/overview), [post validation](/docs/apis/validate/overview), and [post history](/docs/apis/history/overview).
    • Secure social authentication via [JWT](/docs/multiple-users/api-integration-business): your users connect their own accounts and you never handle their credentials.
    ## How the Launch Plan Compares to the Full Business Plan The Launch Plan and full Business Plan share the same API tier and feature set for posting, analytics, comments, DMs, and ads. The differences are about scale and add-ons: | Capability | Launch | Business | | - | - | - | | User Profile cap | Up to 10 | 30+ (scales as needed) | | Core posting, analytics, comments, DMs APIs | Included | Included | | Facebook Ads add-on | Available | Available | | [Max Pack](/docs/additional/maxpack) add-on | Available at a reduced Launch price | Available | | Free trial | 28 days | — | Post quotas, API rate limits, and support are the same on the Launch Plan and the full Business Plan. For exact pricing and the most up-to-date details, see the [Business Plan page](https://www.ayrshare.com/business-plan-for-multiple-users/). ## 28-Day Free Trial The Launch Plan includes a **28-day free trial** so you can integrate the multi-user APIs and validate the flow with real users before committing. The trial gives you the same APIs and profile capacity as a paid Launch Plan subscription. ## Upgrading to the Full Business Plan Because the Launch Plan is capped at 10 User Profiles, you'll need to upgrade to the full [Business Plan](/docs/multiple-users/business-plan-overview) once you're ready to onboard your 11th User Profile. You can upgrade at any time from your [account dashboard](https://app.ayrshare.com). Your existing User Profiles, JWT integration, and API keys carry over. No re-integration required. ## Moving to Launch from the Premium Plan If you're on the Premium Plan and want to move to the Launch Plan, this is a change between plan families (single-user to multi-user). A self-serve option for this isn't available in the dashboard yet — for now, email [support@ayrshare.com](mailto:support@ayrshare.com) and we'll migrate your account to the Launch Plan for you — your connected social accounts, API keys, and settings carry over. > [!NOTE] > > Self-serve dashboard upgrades currently apply only to the monthly Launch Plan > moving to the full Business Plan. Moving from the Premium Plan to the Launch > Plan or Business Plan is handled by support for now, and moving from an annual > Launch Plan to the Business Plan is also support-only. (New customers on the > free Basic Plan can self-serve any plan from the [pricing page](https://www.ayrshare.com/pricing/).) ## Get Started > [card] [Set Up Multi-User Integration](/docs/multiple-users/api-integration-business) > > Generate JWTs and connect your users' social accounts via the API. > [card] [Manage User Profiles](/docs/multiple-users/manage-user-profiles) > > Create, update, and manage the User Profiles included in your plan. > [card] [Full Business Plan Overview](/docs/multiple-users/business-plan-overview) > > Compare the Launch Plan against the full Business Plan and Max Pack add-ons. # Business Plan Overview Source: https://www.ayrshare.com/docs/multiple-users/business-plan-overview Ayrshare Business Plan for managing multiple users, clients, or brands The [Business Plan](https://www.ayrshare.com/business-plan-for-multiple-users/) enables organizations such as companies, agencies, and platforms to manage social media activities including posting, scheduling, analytics, and comment management across multiple users, clients, or brands from a single API integration. Ayrshare is API-first. You build your own front end and call the API to post, fetch analytics, and manage comments on behalf of your users. Your customers connect their own social accounts through a **white-labeled connect page** that you open with a [JWT URL](/docs/multiple-users/api-integration-business#single-sign-on-with-jwt-authentication) — they never log in to Ayrshare and never see your dashboard. > [!WARNING] > > **The dashboard is for your internal team only.** On the Launch, Business, and > Enterprise plans, the [Ayrshare dashboard](/docs/multiple-users/manage-user-profiles#using-the-dashboard) > is where *your team* creates and manages [User Profiles](/docs/multiple-users/manage-user-profiles), > tests integrations, and posts on a client's behalf. Your customers should > **always** be onboarded through the [JWT URL connect page](/docs/multiple-users/api-integration-business#single-sign-on-with-jwt-authentication) — > never give clients dashboard access, as it exposes your account, billing, and > every other profile. (On the single-user Premium plan, you use the dashboard > directly yourself.) > [!NOTE] > > Just getting started with multi-user social? The > [Launch Plan](/docs/multiple-users/business-launch-overview) is the > entry-level Business tier with the same API surface, capped at 10 User Profiles, > and a 28-day free trial. Get more details about the Business Plan by [contacting us](https://www.ayrshare.com/business-plan-for-multiple-users/#contactbusiness). ![Ayrshare Business Plan Overview](https://mintcdn.com/ayrshare-docs/dKBti0xVDJrOZ19f/images/multiple-users/ayrshare-flow.webp?fit=max\&auto=format\&n=dKBti0xVDJrOZ19f\&q=85\&s=9ce0df17ec9cb868150acbc07af082ac) ## Post, Get Analytics, Manage Comments and DMs, and Create Facebook Ads on Behalf of Your Users
    • A single API call to post to your users' [social media networks](/docs/apis/post/overview): Bluesky, Facebook, Google Business Profile (Google My Business), Instagram, LinkedIn, Pinterest, Reddit, Snapchat, Telegram, Threads, TikTok, X, and YouTube.
    • [Send scheduled posts](/docs/apis/post/overview#schedule-posts) of text, images, and videos or [create an auto schedule](/docs/apis/auto-schedule/overview) to post at pre-determined times.
    • Get [detailed analytics](/docs/apis/analytics/overview) on your user's posts, social profile, and links. E.g. number of impressions, follower demographics, average view time of videos, and more.
    • [Add comments](/docs/apis/comments/overview) to a user's post or get all comments made by other users.
    • [Manage direct messages](/docs/apis/messages/overview) across supported platforms.
    • [Create Facebook ads](/docs/apis/ads/overview) from existing posts.
    • Automatically [add hashtags](/docs/apis/hashtags/overview) to posts based on the content, and automatically post your users' [RSS feeds](/docs/apis/feeds/overview).
    • [Validate posts](/docs/apis/validate/overview) before sending to the social networks so your users' social accounts are always protected and don't get in trouble with the social networks.
    • Retrieve a user's [post history](/docs/apis/history/overview), [analytics](/docs/apis/analytics/overview), and [comments](/docs/apis/comments/overview), even on posts that originated outside of Ayrshare.
    ## Seamlessly Integrate with Your Product or Platform
    • Simple and transparent integration for your users, clients, and brand.
    • Make RESTful API calls, or use the Node.js, Python, Bubble.io, or Airtable [packages and integrations](/docs/packages-guides/overview).
    • Enterprise grade security and secure social authentication where you never ask for your users' credentials.
    • Staging environment add-on available for your build workflow.
    ## White-Label the Connect Accounts Page The connect accounts page is the only Ayrshare interface your customers ever see, so make it your own. Add your logo, set custom headers, choose which social networks appear, localize the language, and swap in your own help links — and with the [Max Pack](/docs/additional/maxpack), apply a full custom CSS file, favicon, page title, and footer. Done well, the connect page feels like a native part of your product, not a third-party tool. > [card] [White-Label the Connect Accounts Page](/docs/multiple-users/manage-user-profiles#white-label-the-connect-accounts-page) > > See every branding and customization option available for the connect page. ## Additional Capabilities Available with Max Pack The [Max Pack](/docs/additional/maxpack) provides additional endpoints and other useful utilities for building your app or platform. # Build Your Own Linking Popup Source: https://www.ayrshare.com/docs/multiple-users/connect-direct-mode Direct mode — connect one network at a time from your own button, with a popup you open and watch yourself. Direct mode connects **one social network at a time**, from a button in your own dashboard. You create a link session for that network, open the URL it returns in a popup, and your page hears what happened. Your user never sees a page listing every network, and never leaves your app for longer than the network's own login takes. ## Which Surface You Want Three shapes, and the first two are the same integration. This page is the one you build yourself. | Surface | Your user sees | White-labelling | Choose it when | | - | - | - | - | | **Widget — embedded frames** ([mount](/docs/multiple-users/connect-widget#mount-a-slot)) | our buttons inline in your own layout; no popup until the network's own | **Strongest.** Your page, your fonts and colours, and your user never leaves it | you have a dashboard with a row per network and want linking to happen in place. Max Pack. | | **Widget — your own button** ([popup](/docs/multiple-users/connect-widget#your-own-button)) | your button, then one popup for the network | **Strong.** The popup is ours, but it is brief and inherits your appearance | you want your own button and styling, and no frames in your layout. Max Pack. | | **Hosted linking page** ([how to](/docs/multiple-users/api-integration-business#single-sign-on-with-jwt-authentication)) | a page we host, carrying your logo, colours and custom CSS | **Weakest.** It is our page, and your user leaves yours to use it | you want one link to hand out, or you are onboarding by email. Nothing to build, no Max Pack. | > [!NOTE] > > **Direct mode is the third row's popup without our script.** If your page can load the script, the > [widget](/docs/multiple-users/connect-widget) does everything on this page for you — it opens and > watches the popup itself, and it can embed frames as well. Direct mode is what you want when your > page cannot load a third-party script, or the surface is a native app rather than a browser. ![A customer dashboard with its own Connect buttons and an Ayrshare popup showing the Facebook hand-off screen](https://mintcdn.com/ayrshare-docs/-2tpx3mFy9vWIObd/images/multiple-users/connect-widget-popup.webp?fit=max\&auto=format\&n=-2tpx3mFy9vWIObd\&q=85\&s=b68021d477703d97a5994ee85d299be6) Your button, your page. The popup is the only thing of ours your user sees, and only for as long as the network needs. ## What You Build Four steps. The first is on your server, the rest are in your page. **1. Create a session for one network** From your backend, call [Create a Link Session](/docs/apis/profiles/create-link-session) with `mode: "connect"`, the `network`, and the `origin` your page runs on. ```javascript Your backend theme={"system"} const response = await fetch("https://api.ayrshare.com/api/profiles/link-sessions", { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${process.env.AYRSHARE_API_KEY}`, "Profile-Key": profileKey, }, body: JSON.stringify({ mode: "connect", network: "reddit", origin: "https://app.example.com", }), }); const { url } = await response.json(); ``` You get back a `url` pointing at a single-network connect page, and no `token` — the token is inside the URL. Treat the whole URL like a password: it signs your user into their User Profile. > [!WARNING] > > Create the session on your server, never in the browser. The call needs your API key. **2. Open it in the click handler, synchronously** The popup has to be opened by `window.open` **in the click handler itself**. A browser only permits a popup while it is still processing your user's click, and that permission does not survive an `await` — so fetching the URL first and opening it in the callback is reliably popup-blocked. Fetch the URL when you render the button, or when your user hovers it. By click time you should already have it. ```javascript Your page theme={"system"} // `url` was fetched earlier. Nothing async between the click and window.open. button.addEventListener("click", () => { const popup = window.open(url, "ayrshare-connect", "width=600,height=800"); if (!popup) { showError("Allow popups for this site to connect an account."); return; } listenForOutcome(popup, url); }); ``` **3. Listen for the outcome** Because you passed `origin`, the popup posts an event to your page for each thing that happens in it: `connect:success`, `connect:error`, `connect:cancelled`, and progress events in between. Exactly one of those three arrives per connection. [Link Completion Events](/docs/multiple-users/link-completion-events) has the full event table and a copy-paste listener — `listenForOutcome` above is that snippet. Two parts of it are easy to leave out and both cause real bugs: * **Check `event.origin`** against the origin of the URL you opened. Any page can post a message to your window, and the origin is the only part of a message that cannot be faked. * **Poll `popup.closed`**, with a short grace window before you conclude anything. A popup your user closed by hand sends nothing at all, and without the grace window a successful connection can be reported as cancelled. **4. Handle each ending** | Ending | What it means | What to do | | - | - | - | | `connect:success` | the account is connected **and saved** | refresh that row. A [`GET /user`](/docs/apis/user/profile-details) straight after already shows it. | | `connect:error` | the connection failed | show `message`. `code` is present when the failure has a catalogued code and absent when it does not. | | `connect:cancelled` | your user backed out, or closed the window | leave the row as it was. `reason` is `popupClosed`, `scopesDenied` or `userCancelled`. | | nothing arrives | the link was expired, revoked or unknown, so the page never learned where to send events | poll [Get a Link Session](/docs/apis/profiles/get-link-session), which reports those states authoritatively. | > [!TIP] > > Add `&autoClose=false` to the URL while you are building. The popup then stays open after every > outcome instead of closing itself, so you can read what it says. ## Per-Network Notes Most networks are one popup and nothing else: your user clicks, authorizes at the network, and the popup closes. These are the exceptions worth knowing before you build. > [accordion] X requires your own API keys > > X in direct mode uses **your** X Developer App credentials, supplied when you create the > session as the `X-Twitter-OAuth1-Api-Key` and `X-Twitter-OAuth1-Api-Secret` headers on > [Create a Link Session](/docs/apis/profiles/create-link-session). > > A session created for `twitter` or `x` **without** those headers is refused when the popup > opens: your user is told the connection is not available and is shown no form, and your page > receives `connect:error` with a `message` and **no `code`**. That is deliberate. The missing > credential is yours, not your user's, and end users must never be asked to type your API keys. > > Contrast Bluesky, where the app password is the end user's own credential — that one the > connect page does collect, in a form inside the popup. > [accordion] Facebook shows one button before Meta's login > > `network: "facebook"` shows a single button in the popup, and Meta's own login opens from > that click — Meta requires its login to be started by a click inside the page that hosts its > SDK. Your user clicks twice rather than once; nothing else differs. > > Instagram behaves the same way when it is linked **via a Facebook Page** — that is, when the > session carries `instagramLinkMethod: "facebook"`, or when your account's > [Instagram Login](/docs/multiple-users/manage-user-profiles#instagram-login) setting selects that > flow. With direct Instagram Login there is no extra button. > [accordion] Bluesky and Telegram show page content, not a redirect > > Neither sends your user to a network login. The popup renders content instead: a handle and > app-password form for Bluesky, and a code to use for Telegram. The outcome events are the > same either way. > > X does not belong in this group. With your keys on the session it completes without prompting > your user for anything, and without them it is refused — see above. > [accordion] Telegram finishes out of band > > Telegram shows a code rather than redirecting anywhere, and the connection completes when your > user uses that code — after the popup is gone. There is no browser event to wait for, so poll > [Get a Link Session](/docs/apis/profiles/get-link-session) and watch `completedNetworks`. > [accordion] Facebook Groups cannot be connected this way > > Facebook Groups is not a link target, so `network: "fbg"` returns `code: 508` when you create > the session. > > WhatsApp **is** available in direct mode. It opens Meta's Embedded Signup in the popup, and the > outcome events are the same as any other network. ## Native Apps A native app opens the same `url`, in the **system browser**, and finds out the result by polling [Get a Link Session](/docs/apis/profiles/get-link-session). Set `origin` to your custom scheme (`myapp://connected`) so the page has a way back to your app; a custom scheme cannot receive events, because there is no browser window to post them to. * **iOS** — `ASWebAuthenticationSession`, or `SFSafariViewController`. * **Android** — Chrome Custom Tabs. > [!WARNING] > > **Never open a linking URL in an embedded webview** (`WKWebView`, `UIWebView`, Android > `WebView`). The social networks refuse to authenticate in one: Google rejects the sign-in with > `disallowed_useragent`, and Meta blocks it outright. Your user sees the network's own error page, > not ours, and nothing you can change on your side fixes it. The system browser components above > exist for exactly this reason and keep the user inside your app. ## What Direct Mode Requires
    • The **[Max Pack](/docs/additional/maxpack)**. Creating a connect-mode session without it returns `code: 504`, whatever else the request says.
    • An **`origin`** on every session. There is no allowlist and no registration step — you send it per call. Omitting it returns `code: 505`; a value that is not an `https` origin, a custom scheme, or `http://localhost` returns `code: 506`.
    • A **`network`** that your account has enabled. An unrecognized name returns `code: 508`; a recognized one your account has not enabled returns `code: 509`, which you can fix on your [Social Networks](/docs/multiple-users/manage-user-profiles#set-social-networks-access) page.
    • **Not** `allowedSocial`. It cannot be combined with `network` (`code: 507`) — a single-network session is already its own allowlist.
    Every one of these is in the [Link Session Errors](/docs/errors/errors-ayrshare#link-session-errors) reference, with the message the API returns. ## Next Steps > [card] [Link Completion Events](/docs/multiple-users/link-completion-events) > > Every event the popup sends, and the listener to receive them. ## Related > [card] [Create a Link Session](/docs/apis/profiles/create-link-session#connect-mode) > > The `mode`, `origin` and `network` parameters, and the response shapes. > [card] [Get a Link Session](/docs/apis/profiles/get-link-session) > > Poll for completion when you cannot use a popup. # Ayrshare Connect Widget Source: https://www.ayrshare.com/docs/multiple-users/connect-widget Let your users link social accounts from inside your own dashboard, with one script tag and our buttons embedded in your page. The Ayrshare Connect widget puts our linking buttons **inside your own dashboard**. You load one script, drop a slot wherever a network belongs in your layout, and we render a button there that already shows whether the account is connected. Your user clicks it and links the account without leaving your page — at most one popup, the network's own. You write no popup handling, no OAuth callbacks, no session refresh, and no per-network logic. When a network changes something on their end, the fix ships inside our frames the moment we deploy it; you redeploy nothing. ## Which Surface You Want | Surface | Your user sees | White-labelling | Choose it when | | - | - | - | - | | **Widget — embedded frames** ([`mount()`](#mount-a-slot)) | our buttons inline in your own layout; no popup until the network's own | **Strongest.** Your page, your fonts and colours, and your user never leaves it | you have a dashboard with a row per network and want linking to happen in place. Max Pack. | | **Widget — your own button** ([`popup()`](#your-own-button)) | your button, then one popup for the network | **Strong.** The popup is ours, but it is brief and inherits your appearance | you want your own button and styling, and no frames in your layout. Max Pack. | | **Hosted linking page** ([how to](/docs/multiple-users/api-integration-business#single-sign-on-with-jwt-authentication)) | a page we host, carrying your logo, colours and custom CSS | **Weakest.** It is our page, and your user leaves yours to use it | you want one link to hand out, or you are onboarding by email. Nothing to build, no Max Pack. | ![A customer dashboard with Ayrshare Connect tiles for Instagram, TikTok and LinkedIn embedded in a card](https://mintcdn.com/ayrshare-docs/-2tpx3mFy9vWIObd/images/multiple-users/connect-widget-frames.webp?fit=max\&auto=format\&n=-2tpx3mFy9vWIObd\&q=85\&s=d6c26228a418421772f753f31b0dfc59) Embedded frames: our tiles rendered inside your own layout, one slot per network or one slot for several. ![A customer dashboard with its own Connect buttons and an Ayrshare popup showing the Facebook hand-off screen](https://mintcdn.com/ayrshare-docs/-2tpx3mFy9vWIObd/images/multiple-users/connect-widget-popup.webp?fit=max\&auto=format\&n=-2tpx3mFy9vWIObd\&q=85\&s=b68021d477703d97a5994ee85d299be6) Your own button: you render the button, and one brief popup of ours handles the network. ![The hosted social linking page showing every available network](https://mintcdn.com/ayrshare-docs/-2tpx3mFy9vWIObd/images/multiple-users/connect-widget-hosted.webp?fit=max\&auto=format\&n=-2tpx3mFy9vWIObd\&q=85\&s=4d22a992f6e70808fd9ad9f065ed3879) Hosted linking page: a page we host, carrying your logo and colours, that your user leaves your app to use. > [!NOTE] > > The two widget rows are **one integration**, not two. A single `init` gives you both: mount frames > where you want our buttons, and call `popup()` from your own button everywhere else. They share > one session and report on the same handlers. > > [Direct mode](/docs/multiple-users/connect-direct-mode) is the same popup **without** our script — for > a page with a strict Content-Security-Policy, a server-rendered page, or a native app. There you > open and watch the popup yourself. ## Add the Script Pin a version and its hash, or track a channel without one. Never both — an `integrity` attribute on a moving URL stops working at our next release, because the file it names has legitimately changed. ```html Pinned version theme={"system"} ``` ```html Tracking v1 theme={"system"} ``` | Path | Caching | `integrity` | | - | - | - | | `/ayrshare-connect//widget.js` | immutable, one year | **yes** — pin it | | `/ayrshare-connect/v1/widget.js` | five minutes | no | | `/ayrshare-connect/latest/widget.js` | five minutes | no | **`v1` is the channel to recommend.** It picks up fixes but never crosses a breaking change. `latest` crosses major versions by definition, so it will eventually hand your page a version whose behavior you have not reviewed. Every version's hash is published in [`manifest.json`](https://app.ayrshare.com/ayrshare-connect/manifest.json), which also names what each channel currently serves: ```json manifest.json theme={"system"} { "versions": { "1.2.0": { "integrity": "sha384-qQ/of8CnYj26nF9RkQ6qk3D1vvzWesu7mpBK6MFK2D0EmmgRJHfbDmRowFBfltmz" } }, "latest": "1.2.0", "channels": { "v1": "1.2.0", "latest": "1.2.0" } } ``` Each bundle also opens with a comment naming its own version, which is the fastest way to tell us what a page is actually running: ```js theme={"system"} /*! ayrshare-connect 1.2.0 */ ``` ### Content-Security-Policy If your page sends a Content-Security-Policy, it needs **two** entries, both naming the host you load the script from: ``` script-src https://app.ayrshare.com; frame-src https://app.ayrshare.com; ``` That is the whole list. You need **no `connect-src` entry** for us — our frames reach our API from inside themselves, not from your page — and **no entry for popups**, which are top-level windows your policy does not govern. Both entries were measured: removing `script-src` blocks the script, and removing `frame-src` blocks the frame. ## Start an Instance `session` is the only required option. It is called **once per instance**, not once per mount, and must return your backend's response to [Create a Link Session](/docs/apis/profiles/create-link-session) with `mode: "connect"` — `{ sessionId, token, expiresAt }` — exactly as it came back. ```javascript Your page theme={"system"} const connect = AyrshareConnect.init({ session: () => fetch("/my-api/ayrshare-session").then(r => r.json()), appearance: { "--ayr-connect-accent": "#0B7A6C" }, maxHeight: 800, }); ``` ```javascript Your backend theme={"system"} app.get("/my-api/ayrshare-session", async (req, res) => { const response = await fetch("https://api.ayrshare.com/api/profiles/link-sessions", { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${process.env.AYRSHARE_API_KEY}`, "Profile-Key": profileKeyFor(req.user), }, body: JSON.stringify({ mode: "connect", origin: "https://app.example.com" }), }); res.json(await response.json()); }); ``` A widget session names **no** `network` — it authorizes every network your account permits, and which ones appear is decided per mount, in your page. It does carry an `origin`, which is the one thing that makes the frames render at all: a frame checks the page embedding it against that value and refuses to render anywhere else. > [!WARNING] > > Create the session on your server. The call needs your API key, and the token it returns signs > your user into their User Profile — treat it like a password. We call `session` again before the token expires, so the widget keeps working on a page left open all day. A call that rejects, or returns no token, is retried twice more — after 0.5s, then 1s — before we give up and emit `error`. So one refresh costs at most three calls to your endpoint. | Option | Default | Does | | - | - | - | | `session` | — | **Required.** Returns a connect-mode link session. | | `appearance` | our defaults | Design tokens, applied inside every frame. See [Appearance](#appearance). | | `css` | none | A CSS string applied inside every frame. See [Custom CSS](#custom-css). | | `maxHeight` | `600` | How tall a frame may grow before it scrolls internally instead. | ## Mount a Slot One `mount` per slot. Ask for one network, several, or everything the session permits — the granularity is yours, so a slot can be a single row in an existing table or one panel holding everything. ```javascript theme={"system"} connect.mount("#instagram-slot", { network: "instagram" }); connect.mount("#some-slot", { networks: ["facebook", "tiktok", "x"] }); connect.mount("#everything"); const row = connect.mount(document.querySelector("#tall"), { maxHeight: 1200 }); row.unmount(); ``` `mount` takes a CSS selector or an element, and returns `{ unmount, element }`. It throws if the target matches nothing — which is almost always a slot that does not exist yet, so mount after your markup is in the document. Each mount is one iframe. It reports its own height to us and we resize it to match, so your layout reflows as our content changes; past `maxHeight` the frame scrolls internally instead of running off your page. **The narrowest slot we support is 300px.** Network keys are Ayrshare's own, and the alias spellings work too: `instagram` and `instagramapi` both mean `instagramApi`, and `x` means `twitter`. A key that is not a network renders no tile. ## Your Own Button A customer who would rather use their own button than one of our frames calls `popup` instead. It runs the same flow, on the same session, and reports on the same handlers. > [!WARNING] > > **Call it directly inside the click handler, with nothing awaited before it.** A browser only > permits a popup while it is still processing your user's click, and that permission does not > survive an `await`. Nothing needs awaiting anyway — the session was created at `init`. ```javascript theme={"system"} linkedInButton.addEventListener("click", () => { connect.popup({ network: "linkedin" }); }); ``` `popup` returns `{ close(), network }`, and **always** returns a handle — including after a blocked popup, where `close()` does nothing — so your code never has to null-check it before calling `close()`. Every network works here, including the ones a frame finishes inside its own panel: Facebook shows its hand-off explainer in the popup, Bluesky and X show their credential form, and LinkedIn, Pinterest, YouTube and Google Business go out to the network and come back. It throws synchronously for the three things that are programming errors — no `network`, a destroyed instance, or a session that has not resolved yet. A blocked popup is **not** one of them: that emits `error` with `reason: "popupBlocked"`, because your user did nothing wrong. At most one popup is open at a time. A second call closes the first and reports `cancelled` with `reason: "superseded"` on it. A popup one of our **frames** opened is a different thing and is never touched, so your button cannot cancel a flow running inside a mounted slot. > [!NOTE] > > Whether the session may link a network is the **server's** answer, not the script's. A session > scoped to Bluesky that is asked for LinkedIn gets a refusal rendered in the popup and reported as > `error`. The script only checks that a network was named at all. ## React The script is framework-free, so React needs nothing special from us — but four things about its lifecycle are worth getting right the first time. Load the script **once**, outside your component tree. In Next.js that is `next/script` in your root layout; in Vite or Create React App it is a tag in `index.html`. Loading it per component re-runs it on every mount. ```jsx ConnectAccounts.jsx theme={"system"} import { useEffect, useRef, useState } from "react"; export function ConnectAccounts() { const slot = useRef(null); const [linked, setLinked] = useState([]); useEffect(() => { // Inside the effect, so the ref is attached: mount() throws if its target // does not exist yet, which is exactly what happens if you call it during render. const connect = window.AyrshareConnect.init({ session: () => fetch("/my-api/ayrshare-session").then(r => r.json()), appearance: { "--ayr-connect-accent": "#0B7A6C" }, }); connect.mount(slot.current, { networks: ["instagram", "tiktok", "x"] }); const stop = connect.on("success", ({ network }) => { setLinked(current => [...current, network]); }); // `success` is one of ten events the widget reports. See Events below for // the full list, including `state`, which replaces polling. // destroy() takes the frames, the message listener and the timers with it. // Without this, a route change leaves all three behind. return () => { stop(); connect.destroy(); }; }, []); return
    ; } ``` > [!WARNING] > > **Never reuse an instance after `destroy()`.** A destroyed instance stays destroyed — `popup()` > throws on one, and `mount()` will not bring it back. Create a new instance in the next effect run, > which is what the code above does. Two consequences of that pattern, neither of them a bug: * **In development you will see the session callback fire twice.** React's Strict Mode runs effects mount → unmount → mount, so the instance is created, destroyed and created again. The cleanup above makes that safe; it costs one extra call to your backend in dev and none in production. * **Keep the effect's dependencies stable.** An array literal passed straight into `mount` from a parent render is a new value every time, so an effect that depends on it tears the widget down and rebuilds it on every render. Memoize it, or keep it constant as above. ## Events Subscribe with `on`, which returns an unsubscribe function. Handlers receive the event payload and the mount it came from; `off(name, handler)` does the same job when you would rather name the handler. ```javascript theme={"system"} const stop = connect.on("success", ({ network, displayName }, mount) => { refreshRow(network, displayName, mount.element); }); stop(); connect.on("error", ({ network, code, message }) => report(code, message)); connect.destroy(); // every frame, listener and timer ``` Ten events. Every one carries a `network` except `ready`, and except the one kind of `error` that is not about a network at all — see [`error` has two sources](#error-has-two-sources) below. | Event | Fires when | Also carries | | - | - | - | | `ready` | a frame is mounted and ready | — | | `click` | your user clicked a network, **in either direction** | `action`: `"connect"` or `"unlink"` | | `started` | a link attempt is under way | — | | `selection` | your user reached a picker or a form | `step` | | `success` | the account is connected **and saved** | `displayName` (omitted when unknown), `refId` | | `unlinked` | a connected account was removed, and the removal is saved | — | | `error` | the attempt failed | `code`, `message`, `reason` | | `cancelled` | your user backed out | `reason` | | `closed` | the popup this attempt used has closed | — | | `state` | a network's account state, at mount and on every change | `state`, `since` | **Four of them are endings** — `success`, `unlinked`, `error` and `cancelled` — and exactly one arrives per attempt. `closed` is a lifecycle notice that follows an ending rather than being one. `click` fires **before** any linking work starts, so it reports a click that a blocked popup or a dead session goes on to refuse. It is the event to use for your own analytics; `started` is the one that means an attempt is really running. ### Reasons | Event | `reason` | Means | | - | - | - | | `error` | `popupBlocked` | the browser refused to open the popup | | `cancelled` | `popupClosed` | your user closed the window by hand | | `cancelled` | `scopesDenied` | your user declined a permission the network asked for | | `cancelled` | `userCancelled` | your user backed out, or your code called `handle.close()` | | `cancelled` | `superseded` | a second `popup()` call replaced this attempt | ### `error` Has Two Sources Only one of them is a link failure, and they carry different fields. * A **link error** carries `network`, `code` and the mount it came from. * A **session error** — we could not create or refresh your session — carries only `message`, because nothing was being linked at the time. Destructure defensively: `code` and `network` are `undefined` on the second kind. ### `state` Saves You Polling `state` is the data channel rather than a report on an attempt. Every frame emits one per network as it mounts, carrying that network's current state and the `since` timestamp it has held it from, and another whenever a state changes — **including changes that originate on our side**, such as a token dying into relink-required. So you can drive your whole UI from the widget without polling anything. The values are the same enum [`GET /profiles` with `include=state`](/docs/apis/profiles/get-profiles) returns: `linked`, `unlinked`, `identityVerificationRequired`, `restricted`, `rateLimited`, `suspended`. ## Unlinking Our tiles unlink as well as link. Your user clicks a connected network, confirms, and the account is removed: 1. `click` fires with `action: "unlink"`. 2. `unlinked` fires once the removal is saved. An unlink that **fails** reports `error`, and one your user backs out of at the confirm step reports `cancelled`. There is no separate unlink-failed event. ## Appearance A customer's stylesheet cannot reach inside a cross-origin frame, so styling travels as data we apply within it. Pass `appearance` to `init` as CSS custom properties; each one we do not receive keeps our default. ```javascript theme={"system"} const connect = AyrshareConnect.init({ session: getSession, appearance: { "--ayr-connect-font-family": "Inter, system-ui, sans-serif", "--ayr-connect-surface-bg": "#111318", "--ayr-connect-surface-fg": "#F2F3F7", "--ayr-connect-accent": "#0B7A6C", "--ayr-connect-accent-fg": "#FFFFFF", "--ayr-connect-radius": "12px", }, }); ``` > [!WARNING] > > **Set colors in pairs.** A background token with no foreground token beside it is the one way to > make this produce something unreadable: set `--ayr-connect-surface-bg` to a dark value on its own > and our default `--ayr-connect-surface-fg` is still dark navy. Nothing can infer the other half > for you. With no `appearance` at all, every token holds its default and a frame looks like this: ![Three Ayrshare Connect tiles with the default appearance](https://mintcdn.com/ayrshare-docs/-2tpx3mFy9vWIObd/images/multiple-users/connect-widget-appearance-default.webp?fit=max\&auto=format\&n=-2tpx3mFy9vWIObd\&q=85\&s=62f9ce7e061c8bb58132960b9e270f4a) Default tokens: white surfaces, dark navy text, indigo accent, 8px radius. Pass a handful of tokens and the same frame takes on your palette. This example turns the surfaces pale blue, deepens the text and accent to match, and rounds the corners a little more: ```javascript theme={"system"} const connect = AyrshareConnect.init({ session: getSession, appearance: { "--ayr-connect-surface-bg": "#EFF6FF", "--ayr-connect-border-color": "#BFDBFE", "--ayr-connect-surface-fg": "#0F2A5F", "--ayr-connect-surface-fg-muted": "#4A6A9A", "--ayr-connect-accent": "#1D4ED8", "--ayr-connect-radius": "14px", "--ayr-connect-spacing": "10px", }, }); ``` ![Three Ayrshare Connect tiles restyled with pale blue surfaces and a deeper blue accent](https://mintcdn.com/ayrshare-docs/-2tpx3mFy9vWIObd/images/multiple-users/connect-widget-appearance-themed.webp?fit=max\&auto=format\&n=-2tpx3mFy9vWIObd\&q=85\&s=686d1ad900628a6d4c75dc242f1ab38c) The same three tiles after the tokens above are applied. **There is one palette and no light/dark preset.** Nothing keys on `prefers-color-scheme`, on purpose — your page's theme may not match your user's operating system, and a media query would silently outvote the colors you chose. A dark dashboard is themed by supplying dark values. This token list is a supported contract we keep across versions. ### The Tokens | Token | Default | | - | - | | `--ayr-connect-font-family` | `Inter, "Segoe UI", system-ui, -apple-system, sans-serif` | | `--ayr-connect-font-size` | `16px` | | `--ayr-connect-font-size-sm` | `12px` | | `--ayr-connect-label-font-weight` | `600` | | `--ayr-connect-status-font-size` | `12px` | | `--ayr-connect-status-font-weight` | `600` | | `--ayr-connect-surface-bg` | `#FFFFFF` | | `--ayr-connect-surface-bg-hover` | `#F7F7F7` | | `--ayr-connect-surface-fg` | `#010629` | | `--ayr-connect-surface-fg-muted` | `#56596F` | | `--ayr-connect-border-color` | `#DDDEE2` | | `--ayr-connect-border-width` | `1px` | | `--ayr-connect-radius` | `8px` | | `--ayr-connect-spacing` | `8px` | | `--ayr-connect-accent` | `#4553EE` | | `--ayr-connect-accent-fg` | `#FFFFFF` | | `--ayr-connect-focus-ring-color` | `#4553EE` | | `--ayr-connect-focus-ring-width` | `2px` | | `--ayr-connect-disabled-fg` | `#6E7185` | | `--ayr-connect-status-radius` | `4px` | | `--ayr-connect-status-success-bg` | `#EBFFF8` | | `--ayr-connect-status-success-fg` | `#237C5C` | | `--ayr-connect-status-warning-bg` | `#FFF7EF` | | `--ayr-connect-status-warning-fg` | `#702E00` | | `--ayr-connect-status-critical-bg` | `#FFE5E1` | | `--ayr-connect-status-critical-fg` | `#AD1902` | | `--ayr-connect-status-info-bg` | `#ECF9FF` | | `--ayr-connect-status-info-fg` | `#1F6686` | | `--ayr-connect-callout-bg` | `#FFF7EF` | | `--ayr-connect-callout-fg` | `#702E00` | | `--ayr-connect-partner-name` | `""` | | `--ayr-connect-icon-size` | `32px` | | `--ayr-connect-avatar-size` | `40px` | A value that is not valid CSS for its token is **ignored, with a warning in your console**, rather than applied. That matters more than it sounds: a unitless `8` for `--ayr-connect-spacing` is a perfectly innocent string that would invalidate every calculation reading it and collapse the layout, with no error anywhere. Give lengths a unit. ### Your Own Name on the Hand-Off Screen Before we hand your user to a network, we show a short screen naming who they are connecting through. Two hooks let you make that your own, and both are version-stable. `--ayr-connect-partner-name` sets the label. It is the one token whose value is **text**, so it has to be quoted as a CSS string — an unquoted value is invalid and renders nothing at all: ```javascript theme={"system"} AyrshareConnect.init({ session: getSession, appearance: { "--ayr-connect-partner-name": "'Acme Social'" }, css: "[data-ayr-connect-partner-mark]::after { background-image: url('https://cdn.example.com/mark.svg') }", }); ``` The logo is not a token. We ship the mark as a positioned, sized element and you fill it with a `background-image` through `css`, as above — a token that could fetch an image from inside our document is not something we accept, so the request comes from a rule you wrote rather than from a value you passed us. > [!NOTE] > > `[data-ayr-connect-partner-mark]` and `[data-ayr-connect-partner-name]` are the **exception** to > the custom-CSS caveat below: these two selectors are part of the contract and we keep them across > versions. ### Custom CSS `css` takes a string applied inside every frame, for the cases tokens do not cover. ```javascript theme={"system"} AyrshareConnect.init({ session: getSession, css: "button { letter-spacing: 0.01em }" }); ``` > [!WARNING] > > **Custom CSS is not supported across versions.** Its selectors target our internal markup, which > changes between releases — a rule that works today can silently stop matching after any update. > The token contract above is the part we keep. Pin a version if you depend on custom CSS. Popups inherit the instance's `appearance` and `css` exactly as frames do, so your user does not watch our neutral defaults appear halfway through a flow. ## Worth Knowing Before You Ship > [accordion] A popup whose session has expired reports cancelled, not error > > A popup you opened with `popup()` cannot be told why a token was refused — to say so it would > have to trust an origin it has not yet validated, which our security model does not allow. So a > popup carrying a dead token closes and surfaces as `cancelled` with `reason: "popupClosed"` > rather than `error`. > > In practice this is rare: a popup opened before a silent refresh keeps working, because it > validated its token when it opened. If you see unexplained `popupClosed` results, check that > your `session` endpoint is returning a fresh session. > [accordion] One session per instance, not one per mount > > Fourteen mounts share one token and cost one call to your backend, not fourteen. If you want > differently scoped slots — a different `allowedSocial` on some of them — run a second `init` > with its own session rather than expecting a mount to narrow it. > [accordion] The frames only render on the origin you declared > > Every session carries the `origin` your page runs on, and a frame compares the page embedding it > against that value before rendering anything. A frame embedded somewhere else stays blank and > sends no events. There is no allowlist to register and nothing to configure — send the right > `origin` when you create the session. > [accordion] Height is handled for you, and is not an event > > Frames report their height to the script, and the script resizes them. Your layout just reflows. > There is no resize event to subscribe to, and nothing to measure on your side. ## Requirements
    • The **[Max Pack](/docs/additional/maxpack)**. A widget session is a connect-mode session, and creating one without the Max Pack returns `code: 504`. Contact support if you need connect mode enabled on an account without it.
    • An **`origin`** on every session — the exact origin your page runs on. Omitting it returns `code: 505`; a value that is not an `https` origin, a custom scheme, or `http://localhost` returns `code: 506`.
    • **No `network`** on the session. That parameter is what makes a session [direct mode](/docs/multiple-users/connect-direct-mode) instead, and a direct-mode session's URL is not what the script expects.
    Every code above is in the [Link Session Errors](/docs/errors/errors-ayrshare#link-session-errors) reference, with the exact message the API returns and what to do about it. # Link Completion Events Source: https://www.ayrshare.com/docs/multiple-users/link-completion-events Get told when your user connects an account, instead of polling for it. When you open a linking page in a popup, your own page can listen for what happens in it: your user connected Reddit, they backed out, the connection failed. You get an event per outcome, so your UI updates the moment it happens rather than on a timer. Set [`origin`](/docs/apis/profiles/create-link-session) when you create the link, open the returned `url` in a popup, and listen. Nothing else is required, and nothing changes for links created without `origin` — they behave exactly as they always have. > [!NOTE] > > **Using the [embedded widget](/docs/multiple-users/connect-widget)? This is already wired up for you.** > The script receives these events and hands them to `connect.on("success", …)` with the > `connect:` prefix stripped — no `message` listener, no origin check, no `popup.closed` poll to > write. This page is the wire protocol underneath, and what you build against when **you** own the > window: direct mode, or a popup you open yourself. > [!NOTE] > > Events are sent only to the exact `origin` you set on the link, and only to the window > that opened the popup. Always check `event.origin` in your listener anyway: any page can > post a message to your window, and the origin is the only part of a message that cannot > be faked. ## The Events Every message posted to your page is an object shaped `{ source: "ayrshare", version: 1, event, ... }`, and carries `network` whenever the event is about one. Two events are not: a `connect:closed` from the hosted linking page, where it means your user pressed Done rather than that one connection ended, and the widget's `connect:ready` (see below). The outcomes your own code synthesizes — a blocked popup, or a window your user closed — do not come from us and carry only what you give them. > [!NOTE] > > The [embedded widget](/docs/multiple-users/connect-widget) differs on one event. Its `ready` announces > that a slot is up rather than anything about a network, so it carries no `network` — whereas the > popup's `ready` names the network it was opened for. If you handle both surfaces from one > listener, read `network` defensively on `ready`. > > The widget also adds one `cancelled` reason this surface never sends: `superseded`, when a second > `popup()` call replaces an attempt still in flight. Here there is one window and one outcome, so > there is nothing to supersede. | Event | Extra fields | Sent when | | - | - | - | | `connect:ready` | | The page has loaded and the link has been checked. | | `connect:started` | | Your user has been sent to the social network. | | `connect:selection` | `step` | A picker or a form is on screen — your user has something to do. | | `connect:success` | `refId`, `displayName` | The account is connected **and saved**. `refId` is the User Profile it was connected to. `displayName` is the account name, and is omitted when we do not have one yet. | | `connect:error` | `message`, and `code` when there is one | The connection failed. `code` matches the [error reference](/docs/errors/overview); it is absent when the failure has no catalogued code, and on any outcome your own snippet synthesizes, such as a blocked popup. | | `connect:cancelled` | `reason` | Your user backed out. `reason` is `popupClosed`, `scopesDenied`, or `userCancelled`. | | `connect:closed` | | The popup is about to close itself. Carries no `network` when it comes from the hosted linking page, where it means your user pressed Done rather than that one connection ended. | Exactly one of `connect:success`, `connect:error` or `connect:cancelled` arrives per connection. `connect:closed` is not one of them — it follows, to tell you the popup closed on purpose. `connect:success` is sent only after the account has been saved, so a [`GET /user`](/docs/apis/user/profile-details) immediately after it already shows the connected account. > [!NOTE] > > The popup stays open after `connect:error` so your user can read what went wrong. It > closes itself after `connect:success` and `connect:cancelled`. Add `&autoClose=false` to > the URL to keep it open in every case while you are debugging. ## Listening Two things this snippet does that are easy to leave out. It checks `event.origin`, and it watches for a popup your user closed by hand — a closed window cannot send anything, so polling is the only way to notice. ```javascript theme={"system"} function connectAccount(url) { // Derive the origin from the URL you were given rather than hard-coding one: // if your account uses its own linking domain, the popup runs on that. const popupOrigin = new URL(url).origin; const popup = window.open(url, "ayrshare-connect", "width=600,height=800"); if (!popup) { handleOutcome({ event: "connect:error", message: "The popup was blocked." }); return; } // Declared before anything can call `cleanup`: a message arriving early would // otherwise hit `poll` in its temporal dead zone and throw. let poll; const cleanup = () => { clearInterval(poll); window.removeEventListener("message", onMessage); }; const onMessage = event => { // Both checks, not just the origin: another tab or frame on the same origin // could otherwise post a message your handler would believe. if (event.origin !== popupOrigin || event.source !== popup) return; const message = event.data; if (!message || message.source !== "ayrshare") return; if (["connect:success", "connect:error", "connect:cancelled"].includes(message.event)) { // `finally`, so your own handler throwing cannot leave the poll running — // it would later see the closed popup and report `cancelled` on top of the // outcome you already had. Cleanup also matters after `connect:error`, // where the popup stays open so your user can read it. try { handleOutcome(message); } finally { cleanup(); } } }; window.addEventListener("message", onMessage); // A hand-closed popup sends nothing, so watch for it. Wait a moment before // deciding: the popup closes itself right after sending, and the message can // still be in flight when you first see the window go. let closedAt = null; poll = setInterval(() => { if (!popup.closed) return; if (closedAt === null) { closedAt = Date.now(); return; } if (Date.now() - closedAt < 750) return; cleanup(); handleOutcome({ event: "connect:cancelled", reason: "popupClosed" }); }, 500); } ``` ## Errors `connect:error` carries the same codes as the rest of the API, so a code you see here means what it means everywhere else. The one specific to this surface: | Code | Meaning | | - | - | | `513` | The link was opened as a single-network connect window, but was not created for one. Only reachable if you build that URL yourself — the `url` this endpoint returns always matches the link it created. | Anything else is the social network's own failure, reported with whatever code that failure already has — for example `322` for an Instagram authorization problem, which includes an account that is still Personal rather than Professional. ### A dead link arrives differently A link that is **expired**, **revoked** or **unknown** is refused before the page can learn where to send events, so it cannot send one. Your user sees the reason and its code on screen, and your page hears `connect:cancelled` when they close the window. To tell those apart, poll [Get a Link Session](/docs/apis/profiles/get-link-session): it reports `expired` and `revoked` authoritatively, and returns `502` for a link that does not exist. ## If You Would Rather Not Manage a Popup Two options, and only the second gives up events. **Let the widget own the window.** The [embedded widget](/docs/multiple-users/connect-widget) opens and watches it for you and delivers every event above to your handlers. You still get `success` the moment an account is saved; you just do not write the plumbing. Its frames also mean most networks never open a popup at all until the network's own login. **Poll instead.** If a popup is genuinely not available — a server-rendered app, or a mobile app opening the link in the system browser — poll [Get a Link Session](/docs/apis/profiles/get-link-session). It reports `completedAt` and `completedNetworks` as soon as an account is saved, which is the same moment `connect:success` would have been sent. Telegram always finishes this way, since it completes out of band with no browser callback at all. # Manage Multiple User Profiles Source: https://www.ayrshare.com/docs/multiple-users/manage-user-profiles There are two ways to create and manage multiple account profiles for your organization, users, clients, or brands - via the Dashboard or API. > [!WARNING] > > **The dashboard is for your internal team only.** On the Launch, Business, and > Enterprise plans, the dashboard is where *your team* creates and manages User > Profiles, tests, and posts on a client's behalf. Your customers are **never** > given dashboard access — they connect their social accounts through the > white-labeled [JWT URL connect page](/docs/multiple-users/api-integration-business#single-sign-on-with-jwt-authentication). > In production, create and manage profiles programmatically with the > [API](#using-the-api). (The single-user Premium plan is the exception: there is > no multi-user layer, so you use the dashboard directly yourself.) ## Using the Dashboard If your Ayrshare account is entitled to manage multiple profiles, then you will see the option for **User Profiles** in the left-hand navigation. > [!NOTE] > > If your account does not have the option to manage profiles, then contact your Ayrshare > representative or [contact us](mailto:contact@ayrshare.com) to upgrade. ### Create a New Profile Click the **Add Profile** button and enter your User Profile Title. ![Create a User Profile](https://mintcdn.com/ayrshare-docs/dKBti0xVDJrOZ19f/images/multiple-users/create-profile.webp?fit=max\&auto=format\&n=dKBti0xVDJrOZ19f\&q=85\&s=56ffca9d00547aa746c67769eee87759) ### Switch to the New Profile You will see the new Profile show up in the Active Profiles section. You can now click on the **switch to this profile** icon to switch to that User Profile. ![Switch to a User Profile](https://mintcdn.com/ayrshare-docs/6mPR4kmwNEz1kMbX/images/multiple-users/switch-profile.webp?fit=max\&auto=format\&n=6mPR4kmwNEz1kMbX\&q=85\&s=a3a47332f16b57377b879f37423e38f8) When you switch to a different User Profile, you will see that profile name shown in the top left of the screen under the profile icon. You can always switch back to your primary account profile by clicking the **switch profile** link next to the Primary Profile or by clicking the **Switch to Primary** button at the top of the User Profiles page. From here you are acting as your client/user and can post on their behalf by going to the *Post* page. ### Get the Profile Key The Profile Key is used to post on behalf of the profile with the API. Switch to a [User Profile](/docs/multiple-users/manage-user-profiles) and go to the *Profile Key page* in the left-hand navigation of the Dashboard to get that profile's Key. When making an API call, be sure to include your [API\_KEY](/docs/apis/overview#authorization) and [PROFILE\_KEY](/docs/apis/overview#profile-key-format) in the header. You also receive the Profile Key when you create a new User Profile using the [/profiles/profile endpoint](/docs/apis/profiles/create-profile). This is the preferred method to get the Profile Key. ### Invite a Team Member You can create User Profiles for members of your team. By default, Team Members have an Ayrshare account with full access/admin rights to the dashboard and count as a new User Profile. Please note, you may restrict access by [locking the Primary Profile](/docs/multiple-users/manage-user-profiles#restrict-primary-profile-access). > [!TIP] > > You can only invite team members who have not yet been registered with Ayrshare. If you want to > use an already registered email, please have the user login to the dashboard, go to the Account > page, and click the **Delete** button. In the User Profiles page, switch to your Primary Profile and click the *+ Add Profile* button at the top of the page. In the pop-up modal, click the checkbox "Create a team member profiles with admin rights" box and enter your team member's email address. ![Invite team member](https://mintcdn.com/ayrshare-docs/6mPR4kmwNEz1kMbX/images/multiple-users/team-member.webp?fit=max\&auto=format\&n=6mPR4kmwNEz1kMbX\&q=85\&s=bd920b7ce2fa834579c7073639a8c8d0) Click *Add Team Profile* and an email is sent to the team member's email address with a link to activate their account. #### Team Member Status Once the team member has accepted the invite you will see a green check mark next to the Team Member tag. ![team member accepted](https://mintcdn.com/ayrshare-docs/6mPR4kmwNEz1kMbX/images/multiple-users/team1.webp?fit=max\&auto=format\&n=6mPR4kmwNEz1kMbX\&q=85\&s=08a9aae96eeed3ce0bab912b1e5847f6) If the team member has not yet accepted the invite you may resend it by clicking the mail icon. ![team member resend](https://mintcdn.com/ayrshare-docs/6mPR4kmwNEz1kMbX/images/multiple-users/team2.webp?fit=max\&auto=format\&n=6mPR4kmwNEz1kMbX\&q=85\&s=52b397f9a1fb6761ba77ea73a0222cd2) #### Team Members Contacting Support > [!WARNING] > > For security, only registered team members may contact support on behalf of a business. We will > not be able to assist if they are not registered and verified as a team member of your business. ### Reactivate a Suspended User Profile #### Suspension Reason When a User Profile gets suspended due to a social network violation (such as a [Facebook community standards violation](/docs/help-center/technical-support/facebook-or-instagram-account-restricted)), you will receive a detailed notification email. This email contains important information about the suspension, including the specific reason and the affected User Profile. To locate this notification email: 1. Search your inbox using the RefId of the suspended User Profile 2. The email subject will clearly indicate a suspension has occurred Alternatively, you can view suspension details directly in the dashboard by clicking on the "Suspended" tag that appears next to the affected User Profile. All API calls for that User Profile will return a [403 HTTP status code](/docs/errors/errors-http#403-access-denied). #### Reactivate a Suspended User Profile You may reactivate, i.e. unsuspend, the User Profile by: 1. Logging into the Dashboard and going to the User Profiles page. 2. Find the suspended User Profiles by clicking the "Suspended" tab or searching by the RefId of the User Profile, which is included in the email. 3. Unsuspend the User Profile by clicking the "Unsuspended" button. 4. Review and accept the compliance certification. ![Unsuspend a User Profile](https://mintcdn.com/ayrshare-docs/6mPR4kmwNEz1kMbX/images/multiple-users/suspended.webp?fit=max\&auto=format\&n=6mPR4kmwNEz1kMbX\&q=85\&s=3fcf79aeeeaae389525b1c041b0151dd) For a User Profile's first suspension, the User Profile can be immediately unsuspended once you accept the compliance certification. However, for subsequent suspensions the User Profile will be reinstated *48 hours* after accepting the compliance certification. > [!WARNING] > > We recommend you *do not* use your Primary Profile as a regular User Profile. If the Primary > Profile is suspended, all user profiles will also be suspended. ### Managing Active User Profiles Effective management of User Profiles is essential for maintaining an efficient system in Ayrshare. We recommend implementing a policy to remove inactive User Profiles based on your business requirements. For example, if a user hasn't engaged with your platform by posting content, requesting analytics for some time period, or has not connected any social networks, consider deleting their Ayrshare User Profile, thus keeping your user base current and reducing future costs. You can check if a User Profile has been active in the current month by reviewing their [posting history](/docs/apis/history/overview) and [API usage and connections](/docs/apis/user/overview). If you determine it to be inactive, [delete the User Profile](/docs/apis/profiles/delete-profile). Each monthly billing period is charged on the highest number of User Profiles reached on any single day of that period, and never on fewer than the minimum included in your plan — deleting User Profiles while you are at that minimum does not lower the charge. That high-water mark only ever rises: deleting a User Profile stops later days from pushing it higher, but it cannot bring down a figure the period has already reached, so once your peak is behind you the saving lands in your next billing cycle. This does not apply to the Launch plan, which has a flat price, or to custom and Enterprise agreements, whose own terms apply. Remove inactive User Profiles as soon as you identify them rather than waiting for the period to end. You can see your daily User Profile counts with [Get User Profiles](/docs/apis/profiles/get-profiles) and `actionLog` set to `true`. ## White-Label the Connect Accounts Page The connect accounts (social linking) page is the **only** Ayrshare interface your customers ever see, so you can white-label it to match your brand. Settings below are configured once by your team from the Primary Profile and apply to the page your users open via the [JWT URL](/docs/multiple-users/api-integration-business#single-sign-on-with-jwt-authentication). **Available on the Launch, Business, and Enterprise plans:**
    • [Add or remove your company logo](#update-logo-on-social-linking-page) and set its height.
    • [Choose which social networks](#set-social-networks-access) your users can connect.
    • [Set the page language](#set-language-for-social-linking).
    • [Show your own help links or hide Ayrshare's](#help-links-visible).
    • [Set the top and sub headers](/docs/apis/profiles/create-profile) per User Profile (e.g. the client's name or business).
    • [Control the Close button redirect](/docs/multiple-users/api-integration-business#opening-&-closing-the-social-linking-url) back to your app.
    • [Customize the Instagram and Telegram modals](#custom-modals) with your own HTML.
    **Available with the [Max Pack](/docs/additional/maxpack) (and included with Enterprise):**
    • [Apply a custom CSS file](#customize-css) to control colors, fonts, and which features show, using [documented class hooks](#css-class-hooks) to restyle the connect buttons themselves.
    • [Color the linking dialogs](#linking-dialog-colors) — page pickers, credential forms, and confirmations — to match your page.
    • [Set your own page title, Close button text, and favicon](#change-page-title-close-button-and-favicon).
    • [Add custom footer text or HTML](#footer-text).
    See the [User Integration](/docs/multiple-users/user-integration#social-linking-page-customization) page for how this looks from your user's perspective. ## Settings In the *User Profiles* page select "⚙️ Settings" button to access global settings for your profiles. Be sure to first switch to your *Primary Profile* to access the settings. ![Permission Settings](https://mintcdn.com/ayrshare-docs/6mPR4kmwNEz1kMbX/images/multiple-users/settings%20button.webp?fit=max\&auto=format\&n=6mPR4kmwNEz1kMbX\&q=85\&s=b0d66d82edcd8753cc94987b6bbee520) ### Video Demo of Linking Page Customization Options [Social Account Linking Page Customization Demo](https://www.youtube.com/embed/3RatEh09Qyg) ### Restrict User Profile Access #### Restrict Primary Profile Access Restrict access to the Primary Profile from user and team member profiles by locking the Primary Profile. Activate the Primary Profile lock if you want to prevent team members or user profiles from accessing Profile Settings or the Account/Billing page. ![Primary Profile Lock](https://mintcdn.com/ayrshare-docs/6mPR4kmwNEz1kMbX/images/multiple-users/primary-lock.webp?fit=max\&auto=format\&n=6mPR4kmwNEz1kMbX\&q=85\&s=0c376e9039d943e97042a85d5bc97d0e) When activated team member and user profiles will not be able to switch to the Primary Profile, but may switch to other team member or user profiles. #### Restrict Team Member Access Restrict access to team member profiles by locking their account. This prevents the team member from switching to any other User Profile. Enable the lock by clicking the or icon in the top right of a team member profile. ![Team Member Lock](https://mintcdn.com/ayrshare-docs/6mPR4kmwNEz1kMbX/images/multiple-users/team-member-lock.webp?fit=max\&auto=format\&n=6mPR4kmwNEz1kMbX\&q=85\&s=a64cfba8dbb8a88dd543be1cb834cb1d) You may also lock the team member when you [create the profile](/docs/multiple-users/manage-user-profiles#create-a-new-profile). ### Update Logo on Social Linking Page Add your own logo and set the logo height for the [social linking page](/docs/multiple-users/user-integration). ![Update Profile Logo](https://mintcdn.com/ayrshare-docs/6mPR4kmwNEz1kMbX/images/multiple-users/profile-logo.webp?fit=max\&auto=format\&n=6mPR4kmwNEz1kMbX\&q=85\&s=41fe0dcae1b87e944c2207c129c31ea7) Individual User Profiles can suppress this logo on their own linking page via the `hideLogo` parameter on [Create Profile](/docs/apis/profiles/create-profile) and [Update Profile](/docs/apis/profiles/update-profile). The account-wide logo on every other profile is unaffected. This is useful when an agency white-labels one partner while keeping branding on the rest of their profiles. ### Set Social Networks Access Remove or grant your users/profiles access to the available social networks. By default all social networks are active and available. > [!NOTE] > > Permissions are global across all your user profiles. Disabling only removes the network from your > user's Social Accounts view and does not remove established links. ![Select Social Networks Access](https://mintcdn.com/ayrshare-docs/6mPR4kmwNEz1kMbX/images/multiple-users/select-social-networks.webp?fit=max\&auto=format\&n=6mPR4kmwNEz1kMbX\&q=85\&s=c6ba6926628dd20ebf3c9e391d56b819) See the [create a profile](/docs/apis/profiles/overview#enable-or-disable-social-networks) endpoint for more information on disabling social networks at the User Profile level. ### Set Language for Social Linking Set the language for the social linking page. English is the default. ![Set Language for the Social Linking Page](https://mintcdn.com/ayrshare-docs/6mPR4kmwNEz1kMbX/images/multiple-users/social-linking-language.webp?fit=max\&auto=format\&n=6mPR4kmwNEz1kMbX\&q=85\&s=af0e96e350e8ee8cc31f005a291b2f34) Available languages are:
    • English
    • Arabic
    • Chinese (Simplified)
    • French
    • German
    • Italian
    • Japanese
    • Portuguese
    • Spanish
    If you require a language that is not listed, please contact us. See here for additional translation options:
    • [Translate API Error Message Responses](/docs/errors/errors-ayrshare#error-message-translation)
    • [Translate a Post](/docs/apis/generate/translate-post)
    ### Display Page Location On the social linking page, if a Meta (Facebook or Instagram) or Google Business Profile Page has a location associated with it, display that location information along with the Page name. This additional location context is helpful in scenarios where there are multiple Pages with identical names, as it allows users to differentiate between them more easily. ![Display Page Location](https://mintcdn.com/ayrshare-docs/6mPR4kmwNEz1kMbX/images/multiple-users/display-location.webp?fit=max\&auto=format\&n=6mPR4kmwNEz1kMbX\&q=85\&s=eeb7c04fd2df1c0d6b45a28a77d7e5bc) The Page location, if available, will be displayed below the Page name in the social linking page: ![FB Display Page Location](https://mintcdn.com/ayrshare-docs/6mPR4kmwNEz1kMbX/images/multiple-users/fb%20pages%20with%20location.webp?fit=max\&auto=format\&n=6mPR4kmwNEz1kMbX\&q=85\&s=67e81b3e33683d144492781c09dad604) ### Help Links Visible Enable or disable visibility of the links to the Ayrshare help docs on your users' social linking page. You can also use your own by entering a URL to your publicly available docs. ![Help Links Visible](https://mintcdn.com/ayrshare-docs/6mPR4kmwNEz1kMbX/images/multiple-users/help-links.webp?fit=max\&auto=format\&n=6mPR4kmwNEz1kMbX\&q=85\&s=d2887d2bff0d8a5b132dea6122c1766a) ### Alternative Emails for Alerts Choose different email addresses to deliver alerts, such as unlinked accounts. ![Emails for Alerts](https://mintcdn.com/ayrshare-docs/6mPR4kmwNEz1kMbX/images/multiple-users/email%20alerts.webp?fit=max\&auto=format\&n=6mPR4kmwNEz1kMbX\&q=85\&s=9706511726429bdedc6928a5c10ff71d) Further customization, such as the title can be done via the [/profiles endpoint](/docs/apis/profiles/create-profile). ### Instagram Login Enable or disable Instagram login on the social linking page. ![Instagram Login](https://mintcdn.com/ayrshare-docs/6mPR4kmwNEz1kMbX/images/multiple-users/instagram-login.webp?fit=max\&auto=format\&n=6mPR4kmwNEz1kMbX\&q=85\&s=7423d1ebb6929d4220a244b878fddaf7) Choose whether to enable direct Instagram Login linking that does not require a connected Facebook Page for authentication. If disabled, Instagram linking will require a [connected Facebook Page](/docs/dashboard/connect-social-accounts/facebook) for authentication. > [!WARNING] > > Instagram Login is **enabled by default**. If you require the advanced features listed below > (hashtag search, collaborations, location tagging, or brand data), you must manually disable this > option to use Facebook Page authentication instead. #### Direct Instagram Login vs Facebook Page Authentication **Direct Instagram Login** Instagram API with Instagram Login allows Instagram professionals (businesses and creators) to link their accounts directly without needing an associated Facebook Page. This provides a simplified authentication flow. **Facebook Page Authentication** (traditional method) requires users to first connect a Facebook Page that is linked to their Instagram professional account. #### Feature Limitations with Direct Instagram Login While direct Instagram Login offers convenience, certain features are **not available** when using this method: * **[Brand/User Data](/docs/apis/listen/brand-user#param-instagram-user)** - Cannot retrieve Instagram account information through the brand endpoints. * **[Hashtag Search](/docs/apis/hashtags/search-hashtags)** - Cannot search for hashtags on Instagram. * **[Collaborations](/docs/apis/post/social-networks/instagram#collaboration)** - Cannot invite collaborators to Instagram posts. * **[Location Tagging](/docs/apis/post/social-networks/instagram#location)** - Cannot add location tags to Instagram posts. * **[`idId`- Legacy Instagram User ID](/docs/apis/analytics/social)** - field not returned for Instagram social analytics. #### Which Method Should You Choose? * **Enable Direct Instagram Login** if you only need basic posting functionality and don't require the advanced features listed above. * **Disable Direct Instagram Login** (use Facebook Page authentication) if you need access to all Instagram features, including hashtag search, collaborations, location tagging, or brand data retrieval. > [!NOTE] > > For most clients, the preferred method is to use the direct Instagram Login authentication. If > you're unsure which method to use, we recommend disabling Direct Instagram Login to ensure access > to all features. You can always enable it later if you determine the advanced features are not > needed. ### Custom Modals You can customize the content shown in the Instagram and Telegram modals on the social linking page with your own custom HTML content, such as displaying the content in different languages. For Telegram, your content replaces the modal's default instructions. For Instagram, the modal only appears when you provide custom content — otherwise, clicking Instagram goes straight to the configured login flow: Instagram's own login or, if you have disabled [Instagram Login](#instagram-login), a Facebook login. Your custom content can include formatted text, images, or a combination of both to better suit your needs. ![Custom Modals](https://mintcdn.com/ayrshare-docs/dKBti0xVDJrOZ19f/images/multiple-users/custom-modals.webp?fit=max\&auto=format\&n=dKBti0xVDJrOZ19f\&q=85\&s=67e26724669b23fcc29b20fc9fec1c7c) For example, add your custom text with an anchor link: ```html Custom HTML Modal theme={"system"}
    Everyone agrees that Ayrshare Rocks!
    ``` ```css Your Custom CSS file theme={"system"} span.underline { color: blue; text-decoration: underline; } span.underline a { color: blue; text-decoration: none; } ``` > [!NOTE] > > The HTML must be enclosed in `
    ` tags. > > Style it with class names and put the rules in your [custom CSS file](#customize-css). `