<?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>DEV Community: SocialAPI.tech</title>
    <description>The latest articles on DEV Community by SocialAPI.tech (@socialapitech).</description>
    <link>https://dev.to/socialapitech</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4233697%2Fa3d7bd8e-7220-4440-8f33-4b68af30e44f.png</url>
      <title>DEV Community: SocialAPI.tech</title>
      <link>https://dev.to/socialapitech</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/socialapitech"/>
    <language>en</language>
    <item>
      <title>What's Actually Inside a Tweet's JSON (Field by Field)</title>
      <dc:creator>SocialAPI.tech</dc:creator>
      <pubDate>Sun, 08 Nov 2026 14:59:24 +0000</pubDate>
      <link>https://dev.to/socialapitech/whats-actually-inside-a-tweets-json-field-by-field-2kj8</link>
      <guid>https://dev.to/socialapitech/whats-actually-inside-a-tweets-json-field-by-field-2kj8</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://socialapi.tech/blog/twitter-json-format" rel="noopener noreferrer"&gt;socialapi.tech&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  What's Actually Inside a Tweet's JSON (Field by Field)
&lt;/h1&gt;

&lt;p&gt;You made the call, you got a response, and now you're looking at an object with thirty keys and no idea which ones you can trust.&lt;/p&gt;

&lt;p&gt;That's the real problem with tweet JSON: it isn't complicated, it's &lt;em&gt;conditional&lt;/em&gt;. Half the fields are null most of the time. Three of them look like they mean the same thing. One of them silently changes shape depending on whether the tweet is a retweet.&lt;/p&gt;

&lt;p&gt;This walks through all of it with real responses — what each field holds, when it's null, and the specific places parsers break.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Every response below came from a live call while writing this. If you want to poke at the same shapes yourself, there's a &lt;a href="https://socialapi.tech" rel="noopener noreferrer"&gt;free demo endpoint&lt;/a&gt; that needs no signup.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  The shape, top down
&lt;/h2&gt;

&lt;p&gt;A tweet object has four groups of fields, and keeping them separate makes the rest easier:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Group&lt;/th&gt;
&lt;th&gt;Fields&lt;/th&gt;
&lt;th&gt;Reliability&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Identity&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;id&lt;/code&gt;, &lt;code&gt;url&lt;/code&gt;, &lt;code&gt;conversation_id&lt;/code&gt;, &lt;code&gt;created_at&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Always present&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Content&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;text&lt;/code&gt;, &lt;code&gt;lang&lt;/code&gt;, &lt;code&gt;source&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Always present&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Counters&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;like_count&lt;/code&gt;, &lt;code&gt;retweet_count&lt;/code&gt;, &lt;code&gt;reply_count&lt;/code&gt;, &lt;code&gt;quote_count&lt;/code&gt;, &lt;code&gt;bookmark_count&lt;/code&gt;, &lt;code&gt;view_count&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Always present, often 0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Relationships&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;is_reply&lt;/code&gt;, &lt;code&gt;is_retweet&lt;/code&gt;, &lt;code&gt;is_quote_status&lt;/code&gt;, and their &lt;code&gt;*_id&lt;/code&gt; partners&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Conditional — this is where bugs live&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Entities&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;hashtags&lt;/code&gt;, &lt;code&gt;urls&lt;/code&gt;, &lt;code&gt;photos&lt;/code&gt;, &lt;code&gt;videos&lt;/code&gt;, &lt;code&gt;symbols&lt;/code&gt;, &lt;code&gt;mentioned_users&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Always arrays, often empty&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Author&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;author&lt;/code&gt; (nested object)&lt;/td&gt;
&lt;td&gt;Always present&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Here's a real one, trimmed:&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;"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;"2118565802429632576"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"url"&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://x.com/elonmusk/status/2118565802429632576"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"conversation_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;"2118565802429632576"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"created_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Fri Nov 06 04:47:03 +0000 2026"&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;"RT @AndrThie: This is a bigger deal than people..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"lang"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"en"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Twitter for iPhone"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"is_retweet"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"retweeted_tweet_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;"2118476679593312316"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"is_quote_status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"quoted_tweet_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;"2118381463389307149"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"is_reply"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"in_reply_to_tweet_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"in_reply_to_user"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"in_reply_to_user_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&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;"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;104&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"view_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;217654&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mentioned_users"&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="s2"&gt;"AndrThie"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hashtags"&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;"urls"&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;"photos"&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;"videos"&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;"symbols"&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;"author"&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;"..."&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="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;Look at that object for a second. It's a retweet &lt;strong&gt;and&lt;/strong&gt; a quote. Its &lt;code&gt;like_count&lt;/code&gt; is 0 while &lt;code&gt;retweet_count&lt;/code&gt; is 104. Both of those are correct, and both of them break naive parsers. We'll get to why.&lt;/p&gt;




&lt;h2&gt;
  
  
  Identity fields
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;id&lt;/code&gt; is a string, and it has to be
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&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;"2118565802429632576"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nineteen digits. That exceeds what JavaScript's &lt;code&gt;Number&lt;/code&gt; can represent exactly — anything above 2^53 silently loses precision. Parse that as an integer in JS and you get a different tweet ID than the one you were sent, with no error.&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;// Wrong — silently corrupts the ID&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tweet&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="c1"&gt;// 2118565802429632600  ← note the tail&lt;/span&gt;

&lt;span class="c1"&gt;// Right — leave it a string&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;tweet&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Python handles big integers natively so it won't corrupt, but you still want strings for consistency when the ID crosses into JSON, a URL, or a database column.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;conversation_id&lt;/code&gt; tells you the thread root
&lt;/h3&gt;

&lt;p&gt;For an original tweet, &lt;code&gt;conversation_id === id&lt;/code&gt;. For anything in a thread, it points at the first tweet of that thread. This is how you group a conversation without walking the reply chain upward — and it pairs with &lt;a href="https://socialapi.tech/blog/twitter-advanced-search" rel="noopener noreferrer"&gt;advanced search syntax&lt;/a&gt; when you want a whole thread from one query.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;created_at&lt;/code&gt; is in Twitter's own format
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"created_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Fri Nov 06 04:47:03 +0000 2026"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Not ISO 8601. It's the old &lt;code&gt;EEE MMM dd HH:mm:ss Z yyyy&lt;/code&gt; format, always UTC.&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;from&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;
&lt;span class="n"&gt;dt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strptime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tweet&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;created_at&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;%a %b %d %H:%M:%S %z %Y&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// JS Date parses this correctly without a library&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dt&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;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tweet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;created_at&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  The three relationship flags, and why they're not exclusive
&lt;/h2&gt;

&lt;p&gt;This is the part that causes the most parser bugs, so it gets its own section.&lt;/p&gt;

&lt;p&gt;There are three boolean flags, each with a companion ID field:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Flag&lt;/th&gt;
&lt;th&gt;Companion field&lt;/th&gt;
&lt;th&gt;Means&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;is_reply&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;in_reply_to_tweet_id&lt;/code&gt;, &lt;code&gt;in_reply_to_user&lt;/code&gt;, &lt;code&gt;in_reply_to_user_id&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;This tweet replies to another&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;is_retweet&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;retweeted_tweet_id&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;This is a retweet of another&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;is_quote_status&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;quoted_tweet_id&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;This quotes another tweet&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;They are not mutually exclusive.&lt;/strong&gt; The sample above is simultaneously a retweet and a quote — someone retweeted a tweet that was itself a quote tweet. A reply can also quote. In principle you can hit all three.&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="c1"&gt;# Wrong — assumes one category
&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;tweet&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;is_retweet&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="n"&gt;kind&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;retweet&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;tweet&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;is_quote_status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="n"&gt;kind&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;quote&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;tweet&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;is_reply&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="n"&gt;kind&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;reply&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;kind&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;original&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That &lt;code&gt;elif&lt;/code&gt; chain will classify the sample as a plain retweet and lose the quote relationship entirely. If you're building a thread view or a reply graph, that's a missing edge.&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="c1"&gt;# Right — they're independent facts
&lt;/span&gt;&lt;span class="n"&gt;kinds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;tweet&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;is_retweet&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;      &lt;span class="n"&gt;kinds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;retweet&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;tweet&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;is_quote_status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;kinds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;quote&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;tweet&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;is_reply&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;        &lt;span class="n"&gt;kinds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;reply&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;kinds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;                &lt;span class="n"&gt;kinds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;original&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The companion fields are null when the flag is false.&lt;/strong&gt; Always check the flag before reading the ID, or guard with &lt;code&gt;.get()&lt;/code&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="n"&gt;parent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tweet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;in_reply_to_tweet_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;# None when not a reply
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These flags are normalised the same way on every endpoint we serve, which is the point of a typed response — you write the branch once instead of per-endpoint. You can check the shape against &lt;a href="https://socialapi.tech" rel="noopener noreferrer"&gt;a live response&lt;/a&gt; before writing any of it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Counters: why a retweet shows zero likes
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&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="err"&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;104&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"view_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;217654&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That looks wrong until you know the rule: &lt;strong&gt;engagement belongs to the original tweet, not the retweet.&lt;/strong&gt; When someone retweets, the retweet object carries its own counters, and almost all of them are zero because nobody engages with the retweet itself — they engage with the original.&lt;/p&gt;

&lt;p&gt;If you're ranking by engagement and you don't filter retweets, your top results will be a wall of zeros mixed with real numbers.&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="c1"&gt;# Rank only originals
&lt;/span&gt;&lt;span class="n"&gt;originals&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;tweets&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;is_retweet&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
&lt;span class="n"&gt;originals&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;like_count&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;retweet_count&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;reverse&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two more counter facts worth knowing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;view_count&lt;/code&gt; is the only counter that keeps climbing long after posting.&lt;/strong&gt; Likes and retweets plateau within hours; views don't. If you snapshot engagement, snapshot views separately or you'll conclude the tweet is still "going viral" days later.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;bookmark_count&lt;/code&gt; is often 0 even on large tweets.&lt;/strong&gt; Bookmarks are private; the count is not always exposed. Treat 0 as "unknown", not "nobody bookmarked it".&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're pulling engagement for many tweets at once, fetching them one ID at a time is the expensive way to do it — &lt;a href="https://socialapi.tech/blog/twitter-batch-requests" rel="noopener noreferrer"&gt;batching the requests&lt;/a&gt; changes the arithmetic considerably.&lt;/p&gt;




&lt;h2&gt;
  
  
  Entity arrays: always arrays, usually empty
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"hashtags"&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="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"urls"&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="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"photos"&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="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"videos"&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="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"symbols"&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="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"mentioned_users"&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="s2"&gt;"AndrThie"&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;Six entity fields. The useful property is that &lt;strong&gt;they're always arrays&lt;/strong&gt; — never null, never a bare object. You can iterate without a guard:&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="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;tag&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;tweet&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;hashtags&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;   &lt;span class="c1"&gt;# safe even when empty
&lt;/span&gt;    &lt;span class="bp"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;symbols&lt;/code&gt; holds cashtags (&lt;code&gt;$TSLA&lt;/code&gt;). &lt;code&gt;mentioned_users&lt;/code&gt; holds usernames without the &lt;code&gt;@&lt;/code&gt;. &lt;code&gt;photos&lt;/code&gt; and &lt;code&gt;videos&lt;/code&gt; are separate arrays, so a tweet with mixed media populates both.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One gotcha on &lt;code&gt;text&lt;/code&gt; and &lt;code&gt;urls&lt;/code&gt;:&lt;/strong&gt; the &lt;code&gt;text&lt;/code&gt; field contains the shortened &lt;code&gt;t.co&lt;/code&gt; link, not the destination. If you want the real URL, read it from the &lt;code&gt;urls&lt;/code&gt; array rather than regexing the text.&lt;/p&gt;




&lt;h2&gt;
  
  
  The nested &lt;code&gt;author&lt;/code&gt; object
&lt;/h2&gt;

&lt;p&gt;Every tweet carries its author inline, which saves you a second call:&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="nl"&gt;"author"&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;"44196397"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"username"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"elonmusk"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"display_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Elon Musk"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"bio"&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;"avatar"&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://pbs.twimg.com/profile_images/..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"followers_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;241452659&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"following_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;1421&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"statuses_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;111504&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"created_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Tue Jun 02 20:12:29 +0000 2009"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"location"&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;"url"&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://x.com/elonmusk"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"verified"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"blue_verified"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"verified_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;""&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;Three things people get wrong here:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;username&lt;/code&gt; vs &lt;code&gt;display_name&lt;/code&gt;.&lt;/strong&gt; &lt;code&gt;username&lt;/code&gt; is the handle (stable, used in URLs). &lt;code&gt;display_name&lt;/code&gt; is the shown name (changeable, can be emoji, can be empty). Key your database on &lt;code&gt;id&lt;/code&gt;, not either of them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;verified&lt;/code&gt; vs &lt;code&gt;blue_verified&lt;/code&gt;.&lt;/strong&gt; These are different things. &lt;code&gt;verified&lt;/code&gt; is the legacy checkmark; &lt;code&gt;blue_verified&lt;/code&gt; is the subscription one. In the sample, &lt;code&gt;verified: false&lt;/code&gt; and &lt;code&gt;blue_verified: true&lt;/code&gt; — both correct. Checking only &lt;code&gt;verified&lt;/code&gt; will tell you Elon Musk is unverified.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;location&lt;/code&gt; is free text, often empty.&lt;/strong&gt; It's whatever the user typed. Not geocoded, not validated, frequently a joke. Don't build geography features on it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Null and empty: a quick reference
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;When it's null / empty&lt;/th&gt;
&lt;th&gt;Safe default&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;in_reply_to_*&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;is_reply&lt;/code&gt; is false&lt;/td&gt;
&lt;td&gt;&lt;code&gt;None&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;retweeted_tweet_id&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;is_retweet&lt;/code&gt; is false&lt;/td&gt;
&lt;td&gt;&lt;code&gt;None&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;quoted_tweet_id&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;is_quote_status&lt;/code&gt; is false&lt;/td&gt;
&lt;td&gt;&lt;code&gt;None&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;hashtags&lt;/code&gt; / &lt;code&gt;urls&lt;/code&gt; / &lt;code&gt;photos&lt;/code&gt; / &lt;code&gt;videos&lt;/code&gt; / &lt;code&gt;symbols&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;No such entity&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;[]&lt;/code&gt; (never null)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;author.location&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;User left it blank&lt;/td&gt;
&lt;td&gt;&lt;code&gt;""&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;author.verified_type&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Not an org/gov account&lt;/td&gt;
&lt;td&gt;&lt;code&gt;""&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;bookmark_count&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Often, even on big tweets&lt;/td&gt;
&lt;td&gt;treat 0 as unknown&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A defensive parser that handles all of the above:&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;        &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;                       &lt;span class="c1"&gt;# keep as string
&lt;/span&gt;        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;created&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;   &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strptime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;created_at&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;%a %b %d %H:%M:%S %z %Y&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;text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;      &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text&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;author_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;author&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;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;             &lt;span class="c1"&gt;# key on id, not username
&lt;/span&gt;        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;is_rt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;     &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;is_retweet&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;is_quote&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;is_quote_status&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;is_reply&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;is_reply&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;parent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;    &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;in_reply_to_tweet_id&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;quoted&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;    &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;quoted_tweet_id&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;likes&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;     &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;like_count&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;views&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;     &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;view_count&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;tags&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;      &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;hashtags&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;                 &lt;span class="c1"&gt;# always a list
&lt;/span&gt;        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;verified&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;author&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;verified&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;author&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;blue_verified&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's the whole object handled, and it won't throw on any shape described above.&lt;/p&gt;

&lt;p&gt;Keeping a parser like this working is most of the hidden cost of rolling your own — the field shapes move, and &lt;a href="https://socialapi.tech/blog/twitter-scraper" rel="noopener noreferrer"&gt;each scraping approach&lt;/a&gt; pays that tax differently.&lt;/p&gt;

&lt;p&gt;If you'd rather not maintain this layer at all: our API returns exactly the structure in this article — the same field names, the same null rules — so the parser above runs against it unchanged. &lt;a href="https://socialapi.tech" rel="noopener noreferrer"&gt;Signing up with Google&lt;/a&gt; includes $0.20 in credits, no card.&lt;/p&gt;




&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What format is Twitter's JSON response?&lt;/strong&gt;&lt;br&gt;
A flat object per tweet with a nested &lt;code&gt;author&lt;/code&gt; object. Roughly thirty top-level fields covering identity, content, counters, relationship flags and entity arrays. No envelope around the tweet itself beyond the response wrapper.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why is the tweet ID a string and not a number?&lt;/strong&gt;&lt;br&gt;
Because it's a 19-digit snowflake ID, which exceeds JavaScript's safe integer range (2^53). Parsing it as a number corrupts the last few digits silently. Keep it a string everywhere.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What does conversation_id mean in tweet JSON?&lt;/strong&gt;&lt;br&gt;
It's the ID of the first tweet in the thread. For an original tweet it equals its own &lt;code&gt;id&lt;/code&gt;. Use it to group a whole conversation without walking replies upward one by one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why does created_at not parse as ISO 8601?&lt;/strong&gt;&lt;br&gt;
Twitter uses its own legacy format, &lt;code&gt;EEE MMM dd HH:mm:ss Z yyyy&lt;/code&gt;, always in UTC. Python needs an explicit &lt;code&gt;strptime&lt;/code&gt; format string; JavaScript's &lt;code&gt;new Date()&lt;/code&gt; handles it natively.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can a tweet be a retweet and a quote at the same time?&lt;/strong&gt;&lt;br&gt;
Yes. &lt;code&gt;is_retweet&lt;/code&gt; and &lt;code&gt;is_quote_status&lt;/code&gt; are independent booleans — someone retweeting a quote tweet produces both as true. A reply can also quote. Never treat them as mutually exclusive categories.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why does a retweet show 0 likes?&lt;/strong&gt;&lt;br&gt;
Engagement accrues to the original tweet. The retweet object has its own counters and they're usually zero. Filter retweets out before ranking by engagement, or you'll sort a mix of real numbers and structural zeros.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What's the difference between verified and blue_verified?&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;verified&lt;/code&gt; is the legacy checkmark; &lt;code&gt;blue_verified&lt;/code&gt; is the paid subscription badge. They're separate booleans and an account can have one without the other. Checking only &lt;code&gt;verified&lt;/code&gt; will misclassify most currently-badged accounts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What's the difference between username and display_name?&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;username&lt;/code&gt; is the @handle used in URLs and is relatively stable. &lt;code&gt;display_name&lt;/code&gt; is the shown name, can be changed freely, can contain emoji, and can be empty. Key your storage on the numeric &lt;code&gt;id&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Are the entity arrays ever null?&lt;/strong&gt;&lt;br&gt;
No. &lt;code&gt;hashtags&lt;/code&gt;, &lt;code&gt;urls&lt;/code&gt;, &lt;code&gt;photos&lt;/code&gt;, &lt;code&gt;videos&lt;/code&gt;, &lt;code&gt;symbols&lt;/code&gt; and &lt;code&gt;mentioned_users&lt;/code&gt; are always arrays. When there's no such entity, you get &lt;code&gt;[]&lt;/code&gt;. You can iterate without a null guard.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do I get the real URL instead of the t.co link?&lt;/strong&gt;&lt;br&gt;
Read the &lt;code&gt;urls&lt;/code&gt; array rather than the &lt;code&gt;text&lt;/code&gt; field. The text carries the shortened &lt;code&gt;t.co&lt;/code&gt; form; the array carries the expanded destination.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why is view_count so much higher than other counters?&lt;/strong&gt;&lt;br&gt;
Views count impressions, not actions, and they keep accumulating long after likes and retweets have plateaued. If you snapshot engagement over time, treat views as a separate series.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is bookmark_count accurate?&lt;/strong&gt;&lt;br&gt;
Often not exposed, so it reads 0 even on large tweets. Treat 0 as "unknown" rather than "zero bookmarks".&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What does the source field contain?&lt;/strong&gt;&lt;br&gt;
The client that posted the tweet, as a plain string — &lt;code&gt;"Twitter for iPhone"&lt;/code&gt;, &lt;code&gt;"Twitter Web App"&lt;/code&gt;, or a third-party app name. Useful for filtering automated posting.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is possibly_sensitive?&lt;/strong&gt;&lt;br&gt;
A boolean flag the platform sets on content it considers potentially sensitive. It's a hint for display decisions, not a moderation verdict.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do I tell an original tweet from everything else?&lt;/strong&gt;&lt;br&gt;
All three relationship flags false: not a reply, not a retweet, not a quote. That's the only reliable test — checking any single flag leaves gaps.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does the author object always come with the tweet?&lt;/strong&gt;&lt;br&gt;
Yes, nested inline on every tweet. You don't need a second call to resolve who posted it, which matters when you're processing thousands of tweets.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What's in the symbols array?&lt;/strong&gt;&lt;br&gt;
Cashtags — &lt;code&gt;$TSLA&lt;/code&gt;, &lt;code&gt;$BTC&lt;/code&gt; and similar. Separate from &lt;code&gt;hashtags&lt;/code&gt;, which holds &lt;code&gt;#&lt;/code&gt; tags.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How large is a single tweet object?&lt;/strong&gt;&lt;br&gt;
Typically one to three kilobytes of JSON depending on how many entities and how long the bio is. Worth knowing when you're sizing a batch response or a queue message.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can I rely on these field names staying stable?&lt;/strong&gt;&lt;br&gt;
Field names in a normalised API like ours stay stable by contract. Reading X's internal endpoints directly does not give you that guarantee — the shapes change without notice, which is most of the maintenance cost of a DIY scraper.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What happens to the fields when a tweet is deleted?&lt;/strong&gt;&lt;br&gt;
You stop being able to fetch it at all; you don't get a tombstone object with null fields. Handle the not-found case explicitly rather than expecting a partial record.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do I need an X developer account to see this JSON shape?&lt;/strong&gt;&lt;br&gt;
Not with a third-party read API — you use that provider's key. A developer account is required for official API access, which you'd want if you need to write (post, reply, follow).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How are emoji and non-Latin text encoded in the JSON?&lt;/strong&gt;&lt;br&gt;
As ordinary UTF-8 in the &lt;code&gt;text&lt;/code&gt; field. The catch is &lt;code&gt;display_text_range&lt;/code&gt; and entity indices, which count UTF-16 code units — so an emoji outside the Basic Multilingual Plane counts as 2, not 1. Slicing by Python string index will cut in the wrong place on tweets containing emoji.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why do entity indices not line up with my string slicing?&lt;/strong&gt;&lt;br&gt;
Because X computes them in UTF-16 code units, matching JavaScript's &lt;code&gt;String.length&lt;/code&gt;. In Python, convert the text with &lt;code&gt;text.encode('utf-16-le')&lt;/code&gt; and index on that, or use the entity's own text value instead of slicing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does the JSON include the tweet's language?&lt;/strong&gt;&lt;br&gt;
There's a language code on the tweet, but it's X's own detection and it's wrong often enough on short or mixed-language text that you shouldn't branch on it without a fallback.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can two tweets share the same conversation_id?&lt;/strong&gt;&lt;br&gt;
Yes — that's the point. Every reply in a thread carries the conversation_id of the thread's first tweet, so grouping by it reconstructs the thread.&lt;/p&gt;

</description>
      <category>twitter</category>
      <category>api</category>
      <category>json</category>
      <category>python</category>
    </item>
  </channel>
</rss>
