<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Geoffrey Bourne</title>
    <description>The latest articles on Forem by Geoffrey Bourne (@gbourne).</description>
    <link>https://forem.com/gbourne</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F291368%2Fc1c824e7-f24f-41b1-933b-a5b4b63b4913.jpeg</url>
      <title>Forem: Geoffrey Bourne</title>
      <link>https://forem.com/gbourne</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/gbourne"/>
    <language>en</language>
    <item>
      <title>Twitter API: How to Post and Get Analytics With the Twitter API</title>
      <dc:creator>Geoffrey Bourne</dc:creator>
      <pubDate>Sat, 24 Dec 2022 19:00:34 +0000</pubDate>
      <link>https://forem.com/gbourne/twitter-api-how-to-post-and-get-analytics-with-the-twitter-api-47k8</link>
      <guid>https://forem.com/gbourne/twitter-api-how-to-post-and-get-analytics-with-the-twitter-api-47k8</guid>
      <description>&lt;p&gt;In this tutorial, discover how the Twitter API can enhance your business, and learn how to post and get analytics directly from your platform or app on behalf of your users.&lt;/p&gt;

&lt;p&gt;Despite its recent upheaval, Twitter remains one of the most visible social media platforms with about &lt;a href="https://www.statista.com/statistics/970920/monetizable-daily-active-twitter-users-worldwide/" rel="noopener noreferrer"&gt;238 million daily active users&lt;/a&gt; worldwide. Some of the biggest businesses, brands, and celebrities engage in public conversations with customers and followers every day on Twitter.&lt;/p&gt;

&lt;p&gt;Much of this engagement is done manually using Twitter’s apps, either Twitter built apps or 3rd parties apps. But Twitter also has a robust API (Application Programming interface), which opens up a world of possibilities, e.g. Tweet on behalf of your user right from your platform, present rich analytics such as follower growth, or notify your users when someone comments on their posts.&lt;/p&gt;

&lt;p&gt;Here is a quick guide on how to integrate the Twitter API, send a post, and get the analytics.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Twitter Is Important for Your Business
&lt;/h2&gt;

&lt;p&gt;Businesses are able to engage with customers by using Twitter’s functionality, including promoted tweets, retweets, ads, &lt;a href="https://help.twitter.com/en/using-twitter/spaces" rel="noopener noreferrer"&gt;Twitter Spaces&lt;/a&gt;, curation lists, direct messages, website embeds, and &lt;a href="https://developer.twitter.com/en/docs/twitter-api/annotations/overview" rel="noopener noreferrer"&gt;annotations&lt;/a&gt;. According to Twitter, over &lt;a href="https://business.twitter.com/en/blog/how-valuable-are-your-followers.html" rel="noopener noreferrer"&gt;68% of its users purchase&lt;/a&gt; from a business they follow. Other studies show that &lt;a href="https://business.twitter.com/en/blog/how-twitter-has-become-a-key-customer-support-channel.html" rel="noopener noreferrer"&gt;customers prefer to message&lt;/a&gt; rather than call customer support, and those who get good support on Twitter are likely to spend more on the brand.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.ayrshare.com%2Fwp-content%2Fuploads%2Famazon-twitter-1.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.ayrshare.com%2Fwp-content%2Fuploads%2Famazon-twitter-1.jpg" title="Twitter-Account" alt="alt text" width="635" height="577"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Twitter’s user demographics are also compelling. The number of monetizable users in the U.S. has &lt;a href="https://www.statista.com/statistics/970911/monetizable-daily-active-twitter-users-in-the-united-states/" rel="noopener noreferrer"&gt;steadily increased&lt;/a&gt; every year to 41.5 million in 2022. Given all this, is there a way businesses can utilize Twitter better? That’s where APIs come in.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction To Twitter APIs
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://developer.twitter.com/en/docs/twitter-api" rel="noopener noreferrer"&gt;Twitter REST API&lt;/a&gt; enable web and mobile applications to use Twitter data and features for their unique business needs and customer experiences.&lt;/p&gt;

&lt;p&gt;Here are a few interesting business use cases for a Twitter API:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A hiring platform (like Indeed) can post new jobs from a client on that client’s Twitter account.&lt;/li&gt;
&lt;li&gt;A home real estate platform (like Zillow) can publish videos of new rentals on the landlord’s Twitter account and their ads using the ads API.&lt;/li&gt;
&lt;li&gt;Whenever a shopping season hashtag like “#blackfridaysale” is trending, an e-commerce platform like Shopify can automatically detect it in real-time and tweet the latest products in its inventory.&lt;/li&gt;
&lt;li&gt;A customer service platform like Intercom can automatically search tweets using the search endpoint, and open tickets for a customer who receives user complaints on Twitter.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you’re a developer, we’ll walk you through a step-by-step tutorial to start using the Twitter APIs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prepare To Use The Twitter API
&lt;/h2&gt;

&lt;p&gt;First, you have to figure out what your app needs, and then configure its details in Twitter’s developer portal.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Select the Features and API Versions You Need
&lt;/h3&gt;

&lt;p&gt;Twitter APIs have multiple versions like Twitter API v2, Standard v1.1, and Enterprise APIs (or Gnip). Use the &lt;a href="https://developer.twitter.com/en/docs/twitter-api/getting-started/about-twitter-api#item2" rel="noopener noreferrer"&gt;API resources page&lt;/a&gt; to figure out the list of the versions you’ll need.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.ayrshare.com/top-7-tips-and-tricks-for-twitter-api-posting/" rel="noopener noreferrer"&gt;Top Twitter API tip&lt;/a&gt;: Use Twitter API v2, which is based on &lt;a href="https://graphql.org/" rel="noopener noreferrer"&gt;GraphQL&lt;/a&gt;, for all calls except posting images and videos. Currently only v1.1 supports posting media such as images and videos.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. API Access Level and Approval
&lt;/h3&gt;

&lt;p&gt;Next, decide the API access level you need from Twitter’s &lt;a href="https://developer.twitter.com/en/docs/twitter-api/getting-started/about-twitter-api#item0" rel="noopener noreferrer"&gt;access levels&lt;/a&gt; page. Each level unlocks more features and higher rate limits. You get essential access by default without needing any approval. But approval is required for elevated and academic research access. In general, essential access is sufficient to start, and you can apply for elevated access when needed. Note that approval can take up to two weeks or more.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Register With the Developer Portal
&lt;/h3&gt;

&lt;p&gt;Sign up for a developer account on the &lt;a href="https://developer.twitter.com/en/portal/dashboard" rel="noopener noreferrer"&gt;Twitter developer portal&lt;/a&gt;, and enter the details of your main app.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Configure Settings for Your App
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.ayrshare.com%2Fwp-content%2Fuploads%2Ftwitter-api-developer-portal.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.ayrshare.com%2Fwp-content%2Fuploads%2Ftwitter-api-developer-portal.jpg" title="Twitter Developer Portal" alt="alt text" width="800" height="501"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On logging in, you’ll see your main app under “Projects &amp;amp; Apps.” Select the app and click “Set up” under “User authentication settings.” Configure your app as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;App permissions&lt;/strong&gt;: Read and write.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Type of App&lt;/strong&gt;: If your app can send secrets like the client secret securely, select “Confidential client.” If not, select “Public client.” Typically, traditional client-server web apps are confidential while mobile apps without any servers are public clients.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Callback URI&lt;/strong&gt;: Register the endpoints of your app that Twitter can call during user authorization.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Website URL&lt;/strong&gt;: A link to your web application or its information page.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  5. Store the Client ID and Secret for Your App
&lt;/h3&gt;

&lt;p&gt;After configuration, you’ll get a client ID and client secret for your app. Store them securely in a database, vault, or similar. Either design your application to read them securely, or set up your server to read and inject them into your application’s environment.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Decide on Direct Calls or a Client SDK Package
&lt;/h3&gt;

&lt;p&gt;You can either make all the Twitter REST API calls directly or use one of the many &lt;a href="https://developer.twitter.com/en/docs/twitter-api/tools-and-libraries/v2" rel="noopener noreferrer"&gt;Twitter Client SDK packages&lt;/a&gt;. Our favorite for Node.js is &lt;a href="https://www.npmjs.com/package/twitter-api-v2" rel="noopener noreferrer"&gt;twitter-api-v2&lt;/a&gt; since it supports most features (v1.1 and v2) and is maintained. We also recommend &lt;a href="https://developer.twitter.com/en/docs/tutorials/postman-getting-started" rel="noopener noreferrer"&gt;testing the API calls in Postman&lt;/a&gt;…it makes life a lot easier.&lt;/p&gt;

&lt;p&gt;For the rest of this article, we’ll stick with direct REST API calls.&lt;/p&gt;

&lt;h2&gt;
  
  
  Get Consent From Your Users to Tweet on Their Behalf
&lt;/h2&gt;

&lt;p&gt;To tweet or get analytics on behalf of your users, you must first get their consent. This step is called authorization and is implemented using the OAuth 2.0 authorization code flow. Its outcome is an access token per user that enables your application to call Twitter’s APIs.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Provide a Link or Button to Let Your Users Authorize
&lt;/h3&gt;

&lt;p&gt;By clicking it, your users can initiate the authorization flow. Call your app’s /authorize endpoint when it’s clicked.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Generate the Authorization URL
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.ayrshare.com%2Fwp-content%2Fuploads%2Ftwitter-api-authorization.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.ayrshare.com%2Fwp-content%2Fuploads%2Ftwitter-api-authorization.jpg" title="Twitter OAuth" alt="alt text" width="527" height="935"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In your &lt;code&gt;/authorize&lt;/code&gt; endpoint, create an authorization URL and redirect your user there. It’ll open an authorization page owned by Twitter.&lt;/p&gt;

&lt;p&gt;Authorization URL format:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;https://twitter.com/i/oauth2/authorize?response_type&lt;span class="o"&gt;=&lt;/span&gt;code&amp;amp;scope&lt;span class="o"&gt;=&lt;/span&gt;tweet.read%20tweet.write%20users.read%20offline.access&amp;amp;client_id&lt;span class="o"&gt;={&lt;/span&gt;client-id&lt;span class="o"&gt;}&lt;/span&gt;&amp;amp;redirect_uri&lt;span class="o"&gt;={&lt;/span&gt;registered-url&lt;span class="o"&gt;}&lt;/span&gt;&amp;amp;state&lt;span class="o"&gt;={&lt;/span&gt;state&lt;span class="o"&gt;}&lt;/span&gt;&amp;amp;code_challenge&lt;span class="o"&gt;={&lt;/span&gt;challenge&lt;span class="o"&gt;}&lt;/span&gt;&amp;amp;code_challenge_method&lt;span class="o"&gt;=&lt;/span&gt;S256
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important parameters here are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;redirect_uri={registered-url}&lt;/code&gt;: An endpoint of your web or native app that’s also registered in the developer portal. Twitter sends the authorization code here.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;client_id={client-id}&lt;/code&gt;: Your app’s client ID from the developer portal.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;scope&lt;/code&gt;: The permissions your app needs. Though you just want permission to post, the posting endpoint itself &lt;a href="https://developer.twitter.com/en/docs/twitter-api/tweets/manage-tweets/api-reference/post-tweets" rel="noopener noreferrer"&gt;requires the first three&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Generate the other values using this boilerplate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;randomstring&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;48&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;codeVerifier&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;randomstring&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;128&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;base64Digest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createHash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sha256&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;codeVerifier&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;digest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;base64&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;codeChallenge&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;base64url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromBase64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;base64Digest&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Receive the Authorization Code
&lt;/h3&gt;

&lt;p&gt;Once a user has authorized your app, Twitter sends the authorization code for that user to your app’s &lt;code&gt;redirect_uri&lt;/code&gt; endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;GET https://REDIRECT-URL?code&lt;span class="o"&gt;={&lt;/span&gt;auth-code&lt;span class="o"&gt;}&lt;/span&gt;&amp;amp;state&lt;span class="o"&gt;={&lt;/span&gt;state&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Exchange the Code for an Access Token
&lt;/h3&gt;

&lt;p&gt;Use that authorization code to obtain an access token from the API v2 endpoint, &lt;code&gt;2/oauth2/token&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;POST https://api.twitter.com/2/oauth2/token
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The parameters are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;grant_type=authorization_code&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;code={auth-code}&lt;/code&gt;: The authorization code you received.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;redirect_uri={registered-url}&lt;/code&gt;: Same redirect URL sent earlier.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;code_verifier={verifier}&lt;/code&gt;: The codeVerifier calculated earlier.&lt;/li&gt;
&lt;li&gt;Header &lt;code&gt;Authorization: Basic {client-credentials}&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Header &lt;code&gt;Content-Type: application/x-www-form-urlencoded;charset=UTF-8&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Get the &lt;code&gt;{client-credentials}&lt;/code&gt;by combining the client ID and secret:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;clientCreds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;clientId&lt;/span&gt;&lt;span class="p"&gt;}:&lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;clientSecret&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;clientCreds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;clientCreds&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;base64&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The response JSON:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"access_token"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"…"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"refresh_token"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"…"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"token_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bearer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"expires_in"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;7200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scope"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tweet.read tweet.write users.read offline.access"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This access token expires in two hours. But using the refresh token, you can request new access tokens from the same endpoint.&lt;/p&gt;

&lt;p&gt;You should treat refresh tokens like passwords. Encrypt and store them securely in your database or vault.&lt;/p&gt;

&lt;h2&gt;
  
  
  Post a Tweet With the Twitter API V2
&lt;/h2&gt;

&lt;p&gt;It’s taken us many steps just to get that access token of your user. Luckily, things get easier now.&lt;br&gt;
Post your first request as your user with these parameters and headers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;POST https://api.twitter.com/2/tweets
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;text={tweet}&lt;/code&gt;: The text of your tweet (required)&lt;/li&gt;
&lt;li&gt;Header &lt;code&gt;Authorization: Bearer {access_token}&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Header &lt;code&gt;Content-Type: application/json&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the request succeeds, you receive a 200 (success) with this JSON:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1591766215437361152"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"The text of your tweet"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The ID returned is the Tweet ID and can be used to directly link to the Tweet:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;https://twitter.com/&lt;span class="o"&gt;{&lt;/span&gt;user&lt;span class="o"&gt;}&lt;/span&gt;/status/&lt;span class="o"&gt;{&lt;/span&gt;ID&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But if something’s wrong or missing in the app settings or request headers, it responds with a 403 (forbidden) status:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Forbidden"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"about:blank"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;403&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"detail"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Forbidden"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A tweet request using Node.js with JavaScript looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;fetch&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node-fetch&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Get the access token for the current user.&lt;/span&gt;
&lt;span class="c1"&gt;// from your database / vault / keyring, etc.&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;accessTokenOfUser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getAccessTokenOfLoggedInUser&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Your tweet&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;apiResp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://api.twitter.com/2/tweets&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Authorization&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Bearer&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;accessTokenOfUser&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tweet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;apiResp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Get Analytics With the Twitter API v2
&lt;/h2&gt;

&lt;p&gt;Get metrics for a tweet from the &lt;a href="https://developer.twitter.com/en/docs/twitter-api/tweets/lookup/introduction" rel="noopener noreferrer"&gt;tweets lookup&lt;/a&gt; endpoint by specifying the information you want in &lt;code&gt;tweet.fields&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;GET https://api.twitter.com/2/tweets/&lt;span class="o"&gt;{&lt;/span&gt;ID&lt;span class="o"&gt;}&lt;/span&gt;?tweet.fields&lt;span class="o"&gt;=&lt;/span&gt;public_metrics,organic_metrics,non_public_metrics
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;{ID}&lt;/code&gt;: A tweet ID&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;tweet.fields&lt;/code&gt;: One or more of public_metrics, organic_metrics, non_public_metrics, and promoted_metrics. The &lt;a href="https://developer.twitter.com/en/docs/twitter-api/metrics" rel="noopener noreferrer"&gt;metrics doc&lt;/a&gt; explains them. Use commas to separate multiple values.&lt;/li&gt;
&lt;li&gt;Header &lt;code&gt;Authorization: Bearer {access_token}&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;An example JSON response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1591766215437361152"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"public_metrics"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"retweet_count"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"reply_count"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"like_count"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"quote_count"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"non_public_metrics"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"impression_count"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"user_profile_clicks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"organic_metrics"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"retweet_count"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"impression_count"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"reply_count"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"like_count"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"user_profile_clicks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"The text of your tweet"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;…&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using Node.js with JavaScript:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;fetch&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node-fetch&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;accessTokenOfUser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getAccessTokenOfLoggedInUser&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tweetId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;A-TWEET-ID&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tweetUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;https&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="c1"&gt;//api.twitter.com/2/tweets/${tweetId} +&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;?tweet.fields=public_metrics,organic_metrics,non_public_metrics&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;apiResp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tweetUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Authorization&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Bearer&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;accessTokenOfUser&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;metrics&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;apiResp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  An Easier Alternative to the Twitter API
&lt;/h2&gt;

&lt;p&gt;Congratulations if you got through the long list of steps needed to use Twitter’s API. You can drastically cut down on this by using Ayrshare’s social media API as an &lt;a href="https://docs.ayrshare.com/" rel="noopener noreferrer"&gt;alternative to the Twitter API&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Posting a tweet using our API’s &lt;a href="https://docs.ayrshare.com/rest-api/endpoints/post/twitter" rel="noopener noreferrer"&gt;Twitter integration&lt;/a&gt; and &lt;a href="https://www.npmjs.com/package/social-post-api" rel="noopener noreferrer"&gt;Social API NPM package&lt;/a&gt; is as easy as this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Install Ayrshare’s Node package: npm i social-post-api&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;SocialPost&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;social-post-api&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// 1. Get your API key from https://app.ayrshare.com/api.&lt;/span&gt;
&lt;span class="c1"&gt;// 2. Add it to your application’s environment.&lt;/span&gt;
&lt;span class="c1"&gt;// export AYRSHARE_API_KEY=YOUR-AYRSHARE-API-KEY&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;social&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;SocialPost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AYRSHARE_API_KEY&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;post&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;social&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;post&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Your tweet&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;platforms&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;twitter&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The prerequisites are also simple. Just &lt;a href="https://app.ayrshare.com/" rel="noopener noreferrer"&gt;sign up for Ayrshare&lt;/a&gt;, get your API key, link to your &lt;a href="https://docs.ayrshare.com/gui-dashboard/connect-social-media#twitter-link" rel="noopener noreferrer"&gt;users’ Twitter accounts&lt;/a&gt;, and start calling. You don’t have to struggle with OAuth, app settings, credential security, approval, or Twitter’s docs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.ayrshare.com%2Fwp-content%2Fuploads%2Fayrshare-twitter-api.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.ayrshare.com%2Fwp-content%2Fuploads%2Fayrshare-twitter-api.jpg" title="Ayrshare API Dashboard" alt="alt text" width="800" height="354"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Try out more examples with Ayrshare’s APIs or see our &lt;a href="https://docs.ayrshare.com/packages/overview" rel="noopener noreferrer"&gt;other integrations&lt;/a&gt; such as Python, Airtable, and Bubble.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Tweet With Ayrshare, Node, and Fetch
&lt;/h3&gt;

&lt;p&gt;You don’t have to use our NPM package or other integrations. Calling our API using your preferred HTTP package is just as simple! Here’s an example using the &lt;code&gt;node-fetch&lt;/code&gt; package:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;post&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Your tweet&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;platforms&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;twitter&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;apiResp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://app.ayrshare.com/api/post&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Authorization&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Bearer&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AYRSHARE_API_KEY&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tweet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;apiResp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Post a Twitter Thread With Ayrshare, Python, and Requests
&lt;/h3&gt;

&lt;p&gt;Posting a numbered thread of tweets requires just two additional options. Ayrshare automatically breaks up your lengthy message into a numbered sequence of tweets. Compare this to the Twitter API where you have to post each tweet with details of its previous tweet.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;

&lt;span class="n"&gt;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;post&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Your lengthy essay…&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;platforms&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;twitter&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;twitterOptions&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;thread&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;threadNumber&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Authorization&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Bearer &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;AYRSHARE_API_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Content-Type&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;application/json&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;https://app.ayrshare.com/api/post&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
  &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;tweet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Tweet an Image or Video Using Ayrshare’s Python Package
&lt;/h3&gt;

&lt;p&gt;For images and videos in your user’s tweets, Twitter API expects you to upload them to its &lt;a href="https://developer.twitter.com/en/docs/twitter-api/v1/media/upload-media/overview" rel="noopener noreferrer"&gt;/media/upload endpoint&lt;/a&gt;, keep track of their details, and pass them to the &lt;a href="https://developer.twitter.com/en/docs/twitter-api/tweets/manage-tweets/api-reference/post-tweets" rel="noopener noreferrer"&gt;tweets endpoint&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Using Ayrshare, just set the &lt;code&gt;mediaUrls&lt;/code&gt; option to a list of links.&lt;/p&gt;

&lt;p&gt;Remember that you need a premium, business, or enterprise plan to tweet videos or more than one image.&lt;/p&gt;

&lt;p&gt;If you don’t have a server for your media, you can upload them to our &lt;a href="https://docs.ayrshare.com/rest-api/endpoints/media" rel="noopener noreferrer"&gt;/media API endpoint&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;

&lt;span class="c1"&gt;# python -m pip install -U social-post-api
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;ayrshare&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SocialPost&lt;/span&gt;

&lt;span class="n"&gt;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;post&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Your tweet&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;platforms&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;twitter&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;mediaUrls&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;https://IMAGE–LINK1&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;https://IMAGE-LINK2&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;social&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SocialPost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;AYRSHARE_API_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;social&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Get Analytics for Your Social Posts
&lt;/h2&gt;

&lt;p&gt;How can your users know if their posts and engagements are doing well? You can get detailed metrics for their posts and present the data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;POST https://app.ayrshare.com/api/analytics/post
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;See the &lt;a href="https://docs.ayrshare.com/rest-api/endpoints/analytics" rel="noopener noreferrer"&gt;analytics API reference&lt;/a&gt; for the full response.&lt;/p&gt;

&lt;h2&gt;
  
  
  Boost Your Users’ Online Presence With Twitter API Posting
&lt;/h2&gt;

&lt;p&gt;As a powerful social media platform, Twitter can benefit your customers and drive business. Moreover, by learning the basics of both Twitter and Ayrshare APIs, you can further improve engagement.&lt;/p&gt;

&lt;h2&gt;
  
  
  About the Author
&lt;/h2&gt;

&lt;p&gt;Geoffrey Bourne is co-founder of &lt;a href="https://www.jamdesk.com" rel="noopener noreferrer"&gt;Jamdesk&lt;/a&gt; - the best software documentation platform.&lt;/p&gt;

</description>
      <category>analytics</category>
      <category>api</category>
      <category>automation</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>TikTok API: How to Post to TikTok Using a Social Media API</title>
      <dc:creator>Geoffrey Bourne</dc:creator>
      <pubDate>Mon, 17 Jan 2022 22:43:59 +0000</pubDate>
      <link>https://forem.com/gbourne/tiktok-api-how-to-post-to-tiktok-using-a-social-media-api-83a</link>
      <guid>https://forem.com/gbourne/tiktok-api-how-to-post-to-tiktok-using-a-social-media-api-83a</guid>
      <description>&lt;p&gt;You are probably familiar with the &lt;a href="https://apps.apple.com/us/app/tiktok/id835599320" rel="noopener noreferrer"&gt;official TikTok app&lt;/a&gt; to create and share videos. But, did you know you can also directly post videos using the TikTok API? &lt;/p&gt;

&lt;p&gt;Using a social media API opens worlds of possibilities to create your own app, platform, or service for your users, such as scheduling TikTok posts directly from your platform. &lt;/p&gt;

&lt;p&gt;Ayrshare recently introduced direct TikTok video sharing and enhanced user profile data and analytics. Continue reading to learn about how to use the TikTok API for your own platform.&lt;/p&gt;

&lt;h2&gt;
  
  
  TikTok’s Growth
&lt;/h2&gt;

&lt;p&gt;If you haven’t heard, TikTok is big! TikTok has over 3.3B installs worldwide, &lt;a href="https://www.statista.com/statistics/1267892/tiktok-global-mau/#:~:text=TikTok%20global%20monthly%20active%20users%202018%2D2021&amp;amp;text=In%20September%202021%2C%20social%20video,the%20biggest%20social%20networks%20worldwide." rel="noopener noreferrer"&gt;1B monthly active users&lt;/a&gt;, and in Q4 2021 had the most installs ever in a single quarter. Their growth seems unstoppable and continues to accelerate during the pandemic lockdowns.&lt;/p&gt;

&lt;p&gt;TikTok also has a thriving &lt;a href="https://sensortower.com/solutions/store-intelligence" rel="noopener noreferrer"&gt;ecosystem&lt;/a&gt; of apps that support video creation and downloads. Major new companies that build upon TikTok will emerge – as has happened for Facebook and Instagram. And how did developers create these Facebook and Instagram 3rd party apps? By using an API.&lt;/p&gt;

&lt;h2&gt;
  
  
  Social Media APIs
&lt;/h2&gt;

&lt;p&gt;An API, or Application Programming Interface, gives you, or your developers, the power to create your own app or platform and leverage the capabilities of 3rd parties. You build the experience (GUI) that exactly meets your business needs and on the backend harness the power of external datasets or functionality. &lt;/p&gt;

&lt;p&gt;For example, the popular weather app Dark Skies, now owned by Apple, has a unique interface and capabilities such as letting you know it will rain in 15 minutes. Dark Skies and all other weather apps need to get this weather data from somewhere and I guarantee they don’t have their own network of weather satellites. In the U.S. most weather apps get their data from the National Weather Service via their API and build their own GUI front-end and unique capabilities.&lt;/p&gt;

&lt;p&gt;Social media schedulers do the same thing by using the social media APIs available from social networks such as LinkedIn, Facebook, or Twitter.&lt;/p&gt;

&lt;h2&gt;
  
  
  Share Videos Using the TikTok API
&lt;/h2&gt;

&lt;p&gt;TikTok recently introduced the ability to directly share videos via their &lt;a href="https://developers.tiktok.com/doc" rel="noopener noreferrer"&gt;API&lt;/a&gt; – called the Video Kit. Previously you could only share via an iOS or Android app, but now you can post directly to their endpoint. Side note, there is also a &lt;a href="https://ads.tiktok.com/marketing_api/homepage" rel="noopener noreferrer"&gt;TikTok Marketing API&lt;/a&gt; if you want to manage ads.&lt;/p&gt;

&lt;p&gt;The TikTok social media endpoint uses typical OAuth with tokens. OAuth allows users to authenticate and grant permissions to your all. These grant tokens expire after 24 hours, but can be refreshed with an additional API call. However, after a year the user need to re-authenticate and give permissions again.&lt;/p&gt;

&lt;p&gt;TikTok requires you to request approval and go through a review process for API access, which can take a few days to a few weeks. Since their API endpoint is so new, there are several missing features, such deleting posts, and bugs that might prevent quick development.&lt;/p&gt;

&lt;p&gt;Finally, there are a few other interesting aspects of the TikTok API documentation you might want to explore, such as the &lt;a href="https://developers.tiktok.com/doc/sound-kit-share-sound" rel="noopener noreferrer"&gt;Sound Kit&lt;/a&gt; for sharing sounds or Webhooks to get notified of actions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Alternative Integration
&lt;/h2&gt;

&lt;p&gt;An alternative option to directly integrating with the TikTok API is to integrate with Ayrshare’s social media API which includes &lt;a href="https://docs.ayrshare.com/gui-dashboard/connect-social-media/tiktok-linking" rel="noopener noreferrer"&gt;TikTok integration&lt;/a&gt;. You no longer need approval or worry about the details of TikTok’s evolving API.&lt;/p&gt;

&lt;p&gt;For example, to post a new TikTok video use the following Javascript code with the &lt;a href="https://docs.ayrshare.com/rest-api/endpoints/post#send-a-post" rel="noopener noreferrer"&gt;/post&lt;/a&gt; endpoint. Be sure to replace API_KEY with your key from the &lt;a href="https://app.ayrshare.com/" rel="noopener noreferrer"&gt;dashboard&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;myHeaders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Headers&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;myHeaders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Authorization&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Bearer API_KEY&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;raw&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;post&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Today is a great day!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;platforms&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;tiktok&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mediaUrls&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://images.ayrshare.com/imgs/test-video.mp4&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;requestOptions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;myHeaders&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;follow&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://app.ayrshare.com/api/post&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;requestOptions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is a &lt;a href="https://www.tiktok.com/@borneild/video/7042339075314961711" rel="noopener noreferrer"&gt;TikTok Example&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Or if you prefer to call the Ayrshare TikTok API in Python:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;

&lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://app.ayrshare.com/api/post&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;post&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Today is a great day!&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;platforms&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tiktok&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mediaUrls&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://images.ayrshare.com/imgs/test-video.mp4&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="n"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Authorization&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Bearer API_KEY&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;POST&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After posting, go to your TikTok app to complete the posting by selecting the video and following the instructions.&lt;/p&gt;

&lt;p&gt;Enhanced TikTok User and Post Analytics&lt;br&gt;
In addition to posting videos to TikTok, you also might want analytics information about the user or individual post, such as how many views, shares, or likes.&lt;/p&gt;

&lt;p&gt;For example, to get the user level analytics across all their TikTok videos call the &lt;a href="https://docs.ayrshare.com/rest-api/endpoints/analytics#analytics-on-a-social-network" rel="noopener noreferrer"&gt;/analytics&lt;/a&gt; endpoint. Here is the code in Javascript:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;myHeaders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Headers&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;myHeaders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Authorization&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Bearer API_KEY&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;urlencoded&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URLSearchParams&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;urlencoded&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;platforms[0]&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;tiktok&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;requestOptions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;myHeaders&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;urlencoded&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;follow&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://app.ayrshare.com/api/analytics/social&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;requestOptions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the Python TikTok API code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;

&lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://app.ayrshare.com/api/analytics/social&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;platforms%5B0%5D=tiktok&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;span class="n"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Authorization&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Bearer API_KEY&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;POST&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The user level analytics data returned includes the average video duration and total like, comments, share, and views.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"tiktok"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"analytics"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"durationAverage"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;4.8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"likeCountTotal"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"commentCountTotal"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"shareCountTotal"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;34&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"viewCountTotal"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;193&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you want to get all past video posts and analytics on each, call the &lt;a href="https://docs.ayrshare.com/rest-api/endpoints/history#get-past-posts-instagram-facebook-or-tiktok" rel="noopener noreferrer"&gt;/history&lt;/a&gt; endpoint. The post level analytics data returned includes the share link of the video, description, and counts of likes, comments, shares, and views. this feature is unique to Ayrshare.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"createTime"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1641604664&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"shareUrl"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://www.tiktok.com/@funtime/video/705063834032649?utm_campaign=tt4d_open_api&amp;amp;utm_source=wawnhyitaos7o7"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"videoDescription"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Blah"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"duration"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"7050638340353264943"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Blah"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"embedLink"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://www.tiktok.com/embed/v2/7050638340353264943"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"likeCount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"commentCount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"shareCount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"viewCount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"createTime"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1641603132&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"shareUrl"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://www.tiktok.com/@funtime/video/7050631761763536?utm_campaign=tt4d_open_api&amp;amp;utm_source=wawnhyictaos7o7"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"videoDescription"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Yes"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"duration"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"705063176176353"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Yes"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"embedLink"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://www.tiktok.com/embed/v2/705063176176353"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"likeCount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"commentCount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"shareCount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"viewCount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This insights data can be integrated into your app or platform for your users.&lt;/p&gt;

&lt;h2&gt;
  
  
  About the Author
&lt;/h2&gt;

&lt;p&gt;Geoffrey Bourne is co-founder of &lt;a href="https://www.jamdesk.com" rel="noopener noreferrer"&gt;Jamdesk&lt;/a&gt; - the best software documentation platform.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>python</category>
      <category>programming</category>
    </item>
    <item>
      <title>Best Social Media Posting and Scheduling APIs of 2021</title>
      <dc:creator>Geoffrey Bourne</dc:creator>
      <pubDate>Mon, 16 Aug 2021 23:02:51 +0000</pubDate>
      <link>https://forem.com/gbourne/best-social-media-posting-and-scheduling-apis-of-2021-jno</link>
      <guid>https://forem.com/gbourne/best-social-media-posting-and-scheduling-apis-of-2021-jno</guid>
      <description>&lt;p&gt;Last year we published one of our most popular &lt;a href="https://www.ayrshare.com/best-social-media-posting-and-scheduling-apis-of-2020/" rel="noopener noreferrer"&gt;articles&lt;/a&gt; on the best social media APIs for posting and scheduling. It got a lot of attention from platforms, CMS, agencies, and companies that want to programmatically post on behalf themselves or their users. For 2021, we’re updating the the list and giving more in-depth analysis so you can find the right solutions for your business.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Use a Social Media API?
&lt;/h2&gt;

&lt;p&gt;===========================&lt;/p&gt;

&lt;p&gt;If you want to manually post and schedule social media to networks like Twitter, Instagram, Facebook, LinkedIn, or YouTube, there are some excellent &lt;a href="https://www.techradar.com/best/best-social-media-management-tools" rel="noopener noreferrer"&gt;web-based posting tools&lt;/a&gt; out there. However, if you run an app or platform, say for example a CMS that allows user to create content, your users typically want a simple process to post to social media networks without having to download the content and do it manually. This sharing can be part of a workflow or an automatic process, but the point is you want to make sharing simple and easy for your users without them leaving your platform.&lt;/p&gt;

&lt;p&gt;A social media API makes it possible to programmatically publish from your backend system, allowing you to control the content, posting schedule, management, and reposting on behalf of your users.&lt;/p&gt;

&lt;p&gt;There are two ways to integrate with the networks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;a href="https://www.ayrshare.com/top-10-social-media-apis-for-developers/" rel="noopener noreferrer"&gt;Direct API connection&lt;/a&gt; with each social network. This often requires an approval process at each network and continuous maintenance, but the networks do not charge for standard access.&lt;/li&gt;
&lt;li&gt;  3rd party social media API. While integration is easier and they handle maintenance, there typically is a monthly charge.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here are some 3rd party social media scheduling tools that offer API access in alphabetical order.&lt;/p&gt;

&lt;h2&gt;
  
  
  List of the Top Social Media APIs
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. &lt;a href="https://www.ayrshare.com/" rel="noopener noreferrer"&gt;Ayrshare&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Tagline:&lt;/strong&gt; Powerful APIs that enable you to send social media posts effortlessly. For developers and businesses of all sizes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;API Documentation&lt;/strong&gt;: &lt;a href="https://docs.ayrshare.com/rest-api/overview" rel="noopener noreferrer"&gt;link&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pricing&lt;/strong&gt;: &lt;a href="https://www.ayrshare.com/pricing/" rel="noopener noreferrer"&gt;Free to $14.99/mo for Unlimited Premium&lt;/a&gt;. Free plan offers 8 social accounts and 30 posts a month. Business Plans for multiple users as low as $1.85/mo.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Good:&lt;/strong&gt; In our humble opinion, Ayrshare is the best choice around if you’re looking for a social media API…I mean it is our product and all. We’re the only API-first social media scheduling tool, so everything we build is about working seamlessly with your backend system. That is why we offer numerous &lt;a href="https://docs.ayrshare.com/packages/overview" rel="noopener noreferrer"&gt;SDK packages&lt;/a&gt; and coding examples.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Bad:&lt;/strong&gt; The manual posting dashboard is basic. If you’re looking to spend your day manually posting in a desktop GUI, there are better choices than Ayrshare. In fact, we wrote and article &lt;a href="https://www.ayrshare.com/why-you-shouldnt-use-ayrshare/" rel="noopener noreferrer"&gt;Why You Shouldn’t Use Ayrshare&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. &lt;a href="https://buffer.com/" rel="noopener noreferrer"&gt;Buffer&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Tagline:&lt;/strong&gt; Tell your brand’s story and grow your audience with a publishing, analytics, and engagement platform you can trust.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;API Documentation&lt;/strong&gt;: &lt;a href="https://buffer.com/developers/api" rel="noopener noreferrer"&gt;link&lt;/a&gt; — no new developer accounts &lt;a href="https://buffer.com/developers/api" rel="noopener noreferrer"&gt;since 2019&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pricing&lt;/strong&gt;: &lt;a href="https://buffer.com/pricing/publish" rel="noopener noreferrer"&gt;$15 to $99&lt;/a&gt; a month. Free plan offers 5 social accounts and 10 posts a month.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Good:&lt;/strong&gt; We love Buffer! It was the first tool we used before developing Ayrshare. Buffer’s dashboard is perfectly simple and really a model for all other apps. The API is clear and has all the features needed to schedule posts, with some cool features such as reorder and shuffle future posts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Bad:&lt;/strong&gt; Simple — Buffer shut down new developer access. Unless you already have a developer account, you’re out of luck.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. &lt;a href="https://hootsuite.com/" rel="noopener noreferrer"&gt;Hootsuite&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Tagline:&lt;/strong&gt; Easily manage all your social media and get results with Hootsuite.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;API Documentation&lt;/strong&gt;: &lt;a href="https://platform.hootsuite.com/docs/api/index.html#section/Introduction" rel="noopener noreferrer"&gt;link&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pricing&lt;/strong&gt;: &lt;a href="https://hootsuite.com/plans" rel="noopener noreferrer"&gt;$49 to $599&lt;/a&gt; a month. The Free plan recently was changed to 2 social profiles and 5 posts a month.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Good:&lt;/strong&gt; Hootsuite is one of the most popular scheduling tools. They have a great reputation, feature set, and have been around a long time. Their API is well documented and straight-forward. And if you’re looking to manually post, they have one of the best GUIs around.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Bad:&lt;/strong&gt; Hootsuite’s API has &lt;a href="https://developer.hootsuite.com/docs/api-changelog" rel="noopener noreferrer"&gt;not been updated&lt;/a&gt; in over 2 years and they are lacking many newer integrations, such as Instagram. Also, Hootsuite just &lt;a href="https://www.ayrshare.com/why-did-hootsuite-raise-prices-1200/" rel="noopener noreferrer"&gt;raised prices&lt;/a&gt; for a lot of their users.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. &lt;a href="https://hubspot.com/" rel="noopener noreferrer"&gt;HubSpot&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Tagline:&lt;/strong&gt; Marketing, sales, and service software that helps your business grow without compromise. Because “good for the business” should also mean “good for the customer.”&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;API Documentation&lt;/strong&gt;: &lt;a href="https://legacydocs.hubspot.com/docs/methods/social_media/create_broadcast" rel="noopener noreferrer"&gt;link&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pricing&lt;/strong&gt;: &lt;a href="https://amplifr.com/en/prices/" rel="noopener noreferrer"&gt;$50 to $3,200 a month&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Good:&lt;/strong&gt; HubSpot is a great platform for marketing and they also have an API integration for social posting.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Bad:&lt;/strong&gt; Unfortunately, it looks like HubSpot &lt;a href="https://developers.hubspot.com/docs/api/deprecated-apis" rel="noopener noreferrer"&gt;deprecated their API&lt;/a&gt;. We found the API to be a bit confusing without a clear way to choose a network, such as Twitter or Instagram, to publish the post.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. &lt;a href="https://www.socialoomph.com/" rel="noopener noreferrer"&gt;SocialOomph&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Tagline:&lt;/strong&gt; Boost your productivity with advanced post scheduling tools.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;API Documentation&lt;/strong&gt;: &lt;a href="https://www.socialoomph.com/developers/api/" rel="noopener noreferrer"&gt;link&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pricing&lt;/strong&gt;: &lt;a href="https://www.socialoomph.com/pricing/" rel="noopener noreferrer"&gt;$20 to $83&lt;/a&gt; a month&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Good:&lt;/strong&gt; SocialOomph take a unique approach where each user sets up their open webhook and provides that to the platform. The platform then calls the webhook to post to that user’s account.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Bad:&lt;/strong&gt; The use of webhooks is very technical and might be a lot fo the average customer/user to understand and setup.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. &lt;a href="https://www.socialflow.com/" rel="noopener noreferrer"&gt;Social Flow&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Tagline:&lt;/strong&gt; We help publishers extract more revenue from their best content on social platforms.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;API Documentation&lt;/strong&gt;: Not publicly available&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Good:&lt;/strong&gt; SocialFlow is focused on publishing and advertising for the media industry. It is used by many publishers such as The New York Times, National Geographical Channel, and Fast Company.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Bad:&lt;/strong&gt; Because SocialFlow keeps a tight lipped on pricing and their API, you’ll need to contact them directly.&lt;/p&gt;

&lt;h2&gt;
  
  
  About the Author
&lt;/h2&gt;

&lt;p&gt;Geoffrey Bourne is co-founder of &lt;a href="https://www.jamdesk.com" rel="noopener noreferrer"&gt;Jamdesk&lt;/a&gt; - the best software documentation platform.&lt;/p&gt;

</description>
      <category>api</category>
      <category>social</category>
      <category>programming</category>
    </item>
    <item>
      <title>The Power of Javascript Promise.all()</title>
      <dc:creator>Geoffrey Bourne</dc:creator>
      <pubDate>Tue, 02 Feb 2021 15:42:35 +0000</pubDate>
      <link>https://forem.com/gbourne/the-power-of-javascript-promise-all-12ag</link>
      <guid>https://forem.com/gbourne/the-power-of-javascript-promise-all-12ag</guid>
      <description>&lt;p&gt;This post is not about Promises, async/await, or the single-threaded nature of Javascript. If you want to know about these topics, go &lt;a href="https://web.dev/promises/" rel="noopener noreferrer"&gt;here&lt;/a&gt;, &lt;a href="https://medium.com/javascript-in-plain-english/async-await-javascript-5038668ec6eb" rel="noopener noreferrer"&gt;here&lt;/a&gt;, or &lt;a href="https://blog.bitsrc.io/understanding-asynchronous-javascript-the-event-loop-74cd408419ff" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This article is about the function &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all" rel="noopener noreferrer"&gt;Promise.all()&lt;/a&gt; and how you bring your independently running functions or tasks together into a beautiful result.&lt;/p&gt;

&lt;p&gt;As is often the case, an example or use case does wonders.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use Case: Post to Multiple Social Media Networks
&lt;/h2&gt;

&lt;p&gt;Our app &lt;a href="https://www.ayrshare.com/" rel="noopener noreferrer"&gt;Ayrshare’s&lt;/a&gt; primary purpose is to provide one API to post across multiple social media networks, such as Twitter, Instagram, Facebook, and LinkedIn.&lt;/p&gt;

&lt;p&gt;A user will make an API call (&lt;a href="https://docs.ayrshare.com/rest-api/overview" rel="noopener noreferrer"&gt;RESTful&lt;/a&gt; or via a &lt;a href="https://docs.ayrshare.com/packages/overview" rel="noopener noreferrer"&gt;client package&lt;/a&gt;) with the post text, image, and the list of networks to send the post. The API returns the resulting success or error for each network as an array of results.&lt;/p&gt;

&lt;p&gt;However, there is complexity behind the scenes.&lt;/p&gt;

&lt;p&gt;Each network has its own authorization, workflow, and security requirements. For example, if you attach an image to the post, Twitter requires you to first upload the image, wait for their processing to complete, and send the returned media ID in your a new update status call to Twitter. Timings vary, errors can occur for some and not for others, and new networks are added all the time. But from an end-user perspective, they sent the post and the results are returned right away.&lt;/p&gt;

&lt;p&gt;Here is a visual (&lt;a href="https://www.ayrshare.com/our-firebase-tech-stack/" rel="noopener noreferrer"&gt;Firebase tech stack overview&lt;/a&gt;):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F1evd92qo31rwenyr9de8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F1evd92qo31rwenyr9de8.png" alt="Ayshare Tech Stack" width="800" height="345"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We want to run all the network posts in parallel and collect the results to return to the caller. As the number of networks increases, we ideally want to take no longer than the longest network.&lt;/p&gt;

&lt;p&gt;The problem we face is if each network function call is asynchronous, then each function will complete at different times and we won’t be able to return the results in one response.&lt;/p&gt;

&lt;h2&gt;
  
  
  Promise.all() To The Rescue
&lt;/h2&gt;

&lt;p&gt;If you have a similar problem, Promise.all() is your solution.&lt;/p&gt;

&lt;p&gt;Promise.all() “takes an iterable of promises as an input, and returns a single &lt;code&gt;Promise&lt;/code&gt; that resolves to an array of the results of the input promises.”&lt;/p&gt;

&lt;p&gt;In other words, if you call several async functions, you can wait until they all resolve and capture their output in an array.&lt;/p&gt;

&lt;p&gt;Let’s see a simple example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getSquare&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;printSquares&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;nums&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
     &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;promiseArray&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;nums&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;getSquare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

     &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;promiseArray&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nf"&gt;printSquares&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;printSquare&lt;/em&gt; iterates over an array of ints and squares each one. Calling &lt;em&gt;getSquare&lt;/em&gt; returns a promise because we made it an asynchronous function by adding the keyword &lt;em&gt;async&lt;/em&gt; – technically we don’t need async since the Math.pow function is synchronous.&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;promiseArray&lt;/em&gt; is an array of promises, waiting to be resolved. When we print the &lt;em&gt;promiseArray&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;
   &lt;span class="nb"&gt;Promise&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
   &lt;span class="nb"&gt;Promise&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
   &lt;span class="nb"&gt;Promise&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
   &lt;span class="nb"&gt;Promise&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
   &lt;span class="nb"&gt;Promise&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Close, but what is with those “Promises”? Well, as mentioned above, the array is composed of Promises waiting to be resolved. They can easily be resolved with Promise.all():&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getSquare&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;printSquares&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;nums&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
     &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;promiseArray&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;nums&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;getSquare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

     &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;promiseArray&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
     &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nf"&gt;printSquares&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and the results:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Perfect! Also the results order is maintained.&lt;/p&gt;

&lt;p&gt;Notice that we need to &lt;em&gt;await&lt;/em&gt; on Promise.all() since itself returns a Promise that needs to be resolved.&lt;/p&gt;

&lt;p&gt;For those that are curious, here is a real example of Ayrshare’s code that posts to all the networks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/**
* Post to social networks
*/&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;postNetworks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;NETWORKS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;net&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;platforms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;net&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;net&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;net&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;urls&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{...})&lt;/span&gt;
    &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;processedSocial&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;postNetworks&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Final Thoughts: Parallel Processing
&lt;/h2&gt;

&lt;p&gt;Often Promise.all() is thought of as running in parallel, but this isn’t the case.&lt;/p&gt;

&lt;p&gt;Parallel means that you do many things at the same time on multiple threads.&lt;/p&gt;

&lt;p&gt;However, Javascript is single threaded with one call stack and one memory heap. It is an &lt;a href="https://theflyingmantis.medium.com/javascript-single-threaded-non-blocking-asynchronous-concurrent-language-ffae97c57bef" rel="noopener noreferrer"&gt;asynchronous&lt;/a&gt;, non-blocking language. This means Javascript doesn’t run in parallel, but rather runs only one function/Promise at a time. If the single thread has to wait on something, like the return from an http call, it will move on to another function until the return is complete.&lt;/p&gt;

&lt;p&gt;In the case of our array of Promises, each Promise will be handled one at at time, but Javascript will switch between each one if the processing needs to wait. While the order of Promise resolution in Promise.all() can vary depending upon blocking, the final result will be an array of ordered results.&lt;/p&gt;

&lt;h2&gt;
  
  
  About the Author
&lt;/h2&gt;

&lt;p&gt;Geoffrey Bourne is co-founder of &lt;a href="https://www.jamdesk.com" rel="noopener noreferrer"&gt;Jamdesk&lt;/a&gt; - the best software documentation platform.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>node</category>
      <category>programming</category>
    </item>
    <item>
      <title>Our Firebase Tech Stack</title>
      <dc:creator>Geoffrey Bourne</dc:creator>
      <pubDate>Tue, 08 Dec 2020 15:36:18 +0000</pubDate>
      <link>https://forem.com/gbourne/our-firebase-tech-stack-3g1g</link>
      <guid>https://forem.com/gbourne/our-firebase-tech-stack-3g1g</guid>
      <description>&lt;p&gt;When we started &lt;a href="https://www.ayrshare.com" rel="noopener noreferrer"&gt;Ayrshare&lt;/a&gt; we were keen on using an infrastructure as a service platform and avoid server setup, SSL certs, opening ports, etc. Time to market, right. Amongst the many platforms out there we choose &lt;a href="https://www.firebase.com" rel="noopener noreferrer"&gt;Firebase&lt;/a&gt;. It was an easy decision for us since we are very comfortable with Firebase having built, and even sold, apps built upon it. If not Firebase our second choice would have been &lt;a href="https://www.netlify.com" rel="noopener noreferrer"&gt;Netlify&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Today and Tomorrow Requirements
&lt;/h2&gt;

&lt;p&gt;The first question we asked was, “What we need today?” If came down to a few criteria:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Authentication, ideally having SSO with the major networks&lt;/li&gt;
&lt;li&gt;Hosting a static single page app (React)&lt;/li&gt;
&lt;li&gt;Hosting a landing page. We generally don’t like to build the landing pages in React.&lt;/li&gt;
&lt;li&gt;A database, preferably noSQL&lt;/li&gt;
&lt;li&gt;Back-end running NodeJS, preferably serverless&lt;/li&gt;
&lt;li&gt;Event tracking, e.g. Google Analytics&lt;/li&gt;
&lt;li&gt;Email service&lt;/li&gt;
&lt;li&gt;Payment service&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Second, we asked, “What we might need tomorrow?”&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;iOS and Android apps&lt;/li&gt;
&lt;li&gt;Mobile app push notifications&lt;/li&gt;
&lt;li&gt;ML (machine learning)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Our Stack
&lt;/h2&gt;

&lt;p&gt;We have built on many platform: AWS, Heroku, under our desk (not kidding), and Digital Ocean. We ended up choosing Firebase since it meets a lot of our needs, but certainly not all.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fxl3kskza4y1w4s7lmjna.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fxl3kskza4y1w4s7lmjna.png" alt="stack1" width="800" height="521"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;Firebase is a good choice for SPA React hosting and &lt;a href="https://firebase.google.com/docs/firestore" rel="noopener noreferrer"&gt;Firestore&lt;/a&gt; as a noSQL database. Google Analytics comes built into Firebase. Also, using Firebase’s &lt;a href="https://firebase.google.com/docs/auth/web/firebaseui" rel="noopener noreferrer"&gt;authentication UI&lt;/a&gt; system allowed us to have a secure registration and login process integrated with major SSO providers like Google, Facebook, and GitHub.&lt;/p&gt;

&lt;p&gt;The landing page we built in WordPress hosted on Siteground. There are so many great templates and plugins available for WordPress that we see no reason to recreate the wheel in React. Another option we considered was &lt;a href="https://www.gatsbyjs.org/" rel="noopener noreferrer"&gt;Gatsby&lt;/a&gt; hosted on separate Firebase project.&lt;/p&gt;

&lt;p&gt;Sending emails requires a mail provider and we are accustomed to using Mailgun. Firebase has recently introduced &lt;a href="https://firebase.google.com/products/extensions" rel="noopener noreferrer"&gt;Extensions&lt;/a&gt;, prebuilt cloud functions. One of our favorite is the &lt;a href="https://www.anothermadworld.com/emailing-with-firebase-trigger-email-extension/" rel="noopener noreferrer"&gt;Email Trigger extension&lt;/a&gt; that facilitates sending email from your cloud functions.&lt;/p&gt;

&lt;p&gt;For a payment system, we choose &lt;a href="https://www.stripe.com/" rel="noopener noreferrer"&gt;Stripe&lt;/a&gt;. Stripe has a really nice &lt;a href="https://www.npmjs.com/package/stripe" rel="noopener noreferrer"&gt;NPM package&lt;/a&gt; that makes integration relatively easy.&lt;/p&gt;

&lt;p&gt;Finally, we decided right before launch to add a &lt;a href="https://docs.ayrshare.com/rest-api/endpoints/shorten" rel="noopener noreferrer"&gt;link shortener&lt;/a&gt;, which is very useful when publishing to networks like Twitter that limit the characters. At first we were going to go with bit.ly, but it would soon become more expensive than what we were charging users. We ultimately went with Firebase’s &lt;a href="https://firebase.google.com/products/dynamic-links" rel="noopener noreferrer"&gt;Dynamic Links&lt;/a&gt;, Google’s replacement for goo.gl. Dynamic Links real power are with mobile apps, but it can also be used as a link shortener if you are willing use the RESTful API calls.&lt;/p&gt;

&lt;h2&gt;
  
  
  Firebase Cloud Functions &amp;amp; APIs
&lt;/h2&gt;

&lt;p&gt;Ayrshare is build as an API first platform, so it is critical to have a robust API system. Building a Cloud Function for each API endpoint is not flexible or secure. However, you can add &lt;a href="https://expressjs.com/" rel="noopener noreferrer"&gt;Express&lt;/a&gt; to Cloud Function and it is an excellent and battle tested framework to build and expose APIs.&lt;br&gt;
For example, you can add several security and API facilitating packages such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const express = require("express");
const cors = require("cors");
const bodyParser = require("body-parser");
const helmet = require("helmet");
const rateLimit = require("express-rate-limit");
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you do add Express, you can add the “app” as an http function and export it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const app = express();
exports.api = functions.https.onRequest(app);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then add you typical express functions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app.get("/fun", (req, res) =&amp;gt; {
  console.log("hello fun");
  return res.send('"Hi fun");
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Looking Towards the Future
&lt;/h2&gt;

&lt;p&gt;In the future we plan on building mobile apps, especially for allowing Instagram posting, and will require push notifications. In previous projects we used &lt;a href="https://firebase.google.com/docs/cloud-messaging" rel="noopener noreferrer"&gt;Firebase’s Cloud Messaging (FCM)&lt;/a&gt; system and found it easy, reliable, and free!&lt;br&gt;
And if we really look ahead, we want to add some ML analysis on ideal posting times. The current Firebase ML offering doesn’t seem a great fit, but perhaps it will be in the future.&lt;/p&gt;

&lt;h2&gt;
  
  
  Note on Rate Limit
&lt;/h2&gt;

&lt;p&gt;We had an interesting discussion on &lt;a href="https://www.reddit.com/r/Firebase/comments/iqvoyj/our_firebase_tech_stack/g4wl9zc/?context=3" rel="noopener noreferrer"&gt;Reddit&lt;/a&gt; regarding the use of express-rate-limit. See the code above for details.&lt;br&gt;
We use express-rate-limit to manage API requests per IP so a user doesn’t make 1000 requests a minute. If you use the express-rate-limit default in-memory state storage, every time Cloud Functions re-initializes your code (and cold starts) and all modules are reloaded and the in-memory rate count starts from zero. For example, if you specify the rate limit to be 20 requests per hour and a user hits their limit, but a cold start occurs, the user could make another 20 calls within that hour.&lt;/p&gt;

&lt;p&gt;The right way is to use an external storage for rate limit tracking. Luckly, there are packages that integrate with &lt;a href="https://www.npmjs.com/package/rate-limit-redis" rel="noopener noreferrer"&gt;Redis&lt;/a&gt;, &lt;a href="https://www.npmjs.com/package/rate-limit-memcached" rel="noopener noreferrer"&gt;Memcache&lt;/a&gt;, and &lt;a href="https://www.npmjs.com/package/rate-limit-mongo" rel="noopener noreferrer"&gt;MongoDB&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  About the Author
&lt;/h2&gt;

&lt;p&gt;Geoffrey Bourne is co-founder of &lt;a href="https://www.jamdesk.com" rel="noopener noreferrer"&gt;Jamdesk&lt;/a&gt; - the best software documentation platform.&lt;/p&gt;

</description>
      <category>firebase</category>
      <category>programming</category>
      <category>firestore</category>
      <category>startup</category>
    </item>
  </channel>
</rss>
