SOCMINT on Twitter (X): What Social Media Intelligence Is, and a Worked Method

By TwitterAPI.io Team•22 min read
Six-box cycle diagram titled SOCMINT on X (Twitter): the intelligence cycle, mapped to public-data calls. 1 Question: who amplifies @NASA and when, scope, legal basis. 2 Sources: public X posts and profiles only, no DMs, no protected accounts, no log-in. 3 Collection: /twitter/user/mentions, /twitter/user/last_tweets, /twitter/tweet/advanced_search. 4 Processing: dedupe by id, parse createdAt in UTC, classify reply, quote, original, bucket authors. 5 Analysis: hour heatmap, amplifier concentration, verified and follower mix, hashtag counts. 6 Reporting: aggregates not people, confidence and caveats, retention and deletion, then the next question. Footer: worked example of 500 posts mentioning @NASA and 500 of the organization's own posts, 50 requests, 1,000 posts, $0.15 in API reads, collected in 2026.
The SOCMINT cycle as it runs on X: a question drives sources, collection, processing, analysis and a report that sets the next question. The footer numbers come from the worked exercise below.

Most pages that rank for "socmint" are glossary entries from vendors or a list of social-listening products. They say what the letters stand for and stop. This page treats SOCMINT as what its authors meant it to be: a discipline with a definition, a lineage, a place next to OSINT, HUMINT and SIGINT, a cycle you can run, a legal boundary you can state, and a cost you can calculate. It stays on one platform, X (Twitter), because the method only becomes concrete when it meets real fields and real endpoints.

It is the companion to our Twitter OSINT page. That page is the inventory: which public X field answers which investigative question, the Snowflake timestamp formula, the account-age and ID tools, search operators, what the API can and cannot return about deleted posts. We will not repeat any of that here. This page is about the discipline those fields serve: how a SOCMINT question is framed, what the open route may and may not touch, and how a collection plan turns into a defensible aggregate finding.

To keep it honest we ran one small, end-to-end exercise and show every step: 500 public posts mentioning @NASA and 500 of @NASA's own posts, collected in 2026 with two endpoints, 50 requests and $0.15 of API reads. No individual is named or profiled; the output is a posting-hour comparison, a breakdown of what a mention is, a concentration measure for repeat accounts, and buckets for the accounts behind the mentions. The same code, pointed at a different organization or a keyword, is the first SOCMINT product most analysts are asked for.

01 — Section

What SOCMINT is, and where the term comes from

SOCMINT stands for social media intelligence: the systematic collection and analysis of what people publish on social platforms, in order to answer a question someone has to act on. The emphasis is on the question and the analysis. A pile of posts is data; a statement such as "the conversation about this agency is driven by sixty accounts, mostly replies, peaking two hours before the agency's own posting peak, with low confidence on who those accounts represent" is intelligence. Everything on this page serves that transformation.

The term has a traceable origin. In April 2012 the London think tank Demos published #Intelligence, written by Sir David Omand (a former director of GCHQ), Jamie Bartlett and Carl Miller. Its executive summary says that "social media intelligence, which we term SOCMINT, could contribute decisively to public safety: identifying criminal activity; giving early warning of disorder and threats to the public; or building situational awareness in rapidly changing situations." The paper describes itself as "the first effort to examine the ethical, legal and operational challenges involved in using social media for intelligence and insight purposes", and argues that SOCMINT "must be based on a publicly argued, legal footing". We verified these quotes against the PDF on demos.co.uk this session.

The paper's lasting contribution is not the acronym but two structural ideas. The first is a set of six principles for any state use of SOCMINT, reproduced here because they transfer almost unchanged to a corporate or research setting.

Demos principle (2012, verbatim)What it means for an X investigation
1. there must be sufficient, sustainable causea stated question and a stated harm or decision it serves; "let's see what we find" is not cause
2. there must be integrity of motivethe justification, the collection and the write-up must agree; no collecting for one purpose and reporting for another
3. the methods used must be proportionate and necessarythe smallest sample and the fewest fields that can answer the question
4. there must be right authority, validated by external oversightsomebody signs off, and the sign-off is reviewable: a protocol file, a ticket, a DPIA
5. recourse to secret intelligence must be a last resort if more open sources can be usedif a public mention timeline answers it, nothing intrusive is justified
6. there must be reasonable prospect of successknow what the sample cannot show before you collect it, and say so in the report

The second idea is the two-route split. The paper recommends "a clear distinction between open source non-intrusive SOCMINT and intrusive or surveillance SOCMINT": route one, "open source non-intrusive SOCMINT, which can be conducted on a similar basis to non-state actors, such as universities and commercial companies", and route two, "the exercise of state-specific powers of access intended to result in the identification of individuals and access to private information". Everything on this page is route one. The moment a task requires identifying a person, reading private messages or gaining access the platform does not give an ordinary user, it has become route two, and in that world the question is no longer which endpoint to call but whether you have the authority at all.

02 — Section

SOCMINT vs OSINT vs HUMINT vs SIGINT

The intelligence disciplines are named by source, not by subject. That is why the comparison below is about where the material comes from and what you had to do to get it, not about what the material is used for.

DisciplineSourceWhat you do to obtain itOn X, in practice
OSINT (open source)anything lawfully available to the public: web, records, media, data setsfind, read, cross-referenceall of X's public surface plus everything outside it
SOCMINT (social media)public activity on social platforms: posts, profiles, follow graph, engagement, timingcollect at scale, classify, measurepublic posts, profiles, followers and followings, mentions, search
HUMINT (human)a person who tells you somethingelicit, interview, recruitmessaging an account to ask a question is HUMINT, not SOCMINT
SIGINT (signals)intercepted communications or their metadataintercept, with legal authorityprivate messages and non-public traffic; off limits to the open route

SOCMINT is therefore a subset of OSINT narrowed by source, and the distinction matters for two reasons. Volume: an OSINT desk reads dozens of documents; a SOCMINT exercise classifies thousands of posts, so the method is statistical and the output is an aggregate. Boundary: SOCMINT lives next to two things it must not become. Sending a message to an account to get an answer is HUMINT and carries its own rules (and on X it means acting as an account, with the platform's limits on that account). Reading messages the sender did not publish is interception. The open route touches neither.

A practical consequence: the fields a SOCMINT analyst relies on are the ones every logged-out visitor can see, just read by machine. On X that is the post (text, createdAt, lang, isReply, inReplyToUsername, quoted_tweet, retweeted_tweet, entities, extendedEntities, the engagement counts), and the public profile embedded in each post as author (userName, followers, following, createdAt, isBlueVerified, isAutomated, statusesCount). Our Twitter OSINT page maps those fields to investigative questions one by one; this page assumes that map and uses it.

03 — Section

The SOCMINT cycle: question, sources, collection, processing, analysis, reporting

The cycle is the classic intelligence cycle with social-platform specifics at each stage. Run it in order; skipping the first stage is how a collection turns into a fishing expedition.

Question. Write the question as a sentence someone will act on, with a time window and a unit of analysis. "Who drives the public conversation about this organization on X, when does it happen relative to the organization's own posting, and how much of it is a small core?" is a SOCMINT question. "Monitor the organization" is not. The question fixes the sample size: a posting-hour pattern needs a few hundred posts; a share-of-replies estimate at a few points' precision needs about five hundred; a claim about a trend over months needs a window, not a snapshot.

Sources. Decide which public surfaces answer the question and write down which you will not touch. For the worked example: the organization's own public timeline and the public posts that mention it. Not touched: direct messages, protected accounts, anything that requires being logged in as someone, and the identities of the private individuals behind the mentions.

Collection. Map each source to an endpoint, estimate the request count and the cost before the first call, and write the protocol down. On twitterapi.io the three collection calls used here are /twitter/user/mentions (public posts mentioning an account, 20 a page, newest first, with sinceTime and untilTime in Unix seconds for a window), /twitter/user/last_tweets (an account's own timeline, 20 a page, newest first, reposts included) and /twitter/tweet/advanced_search (any X search operator, queryType=Latest or Top), each paginated with has_next_page and next_cursor. Tweet endpoints bill 15 credits per post returned, which is $0.15 per 1,000 posts, with a 15-credit minimum per request; no X developer account or log-in is needed for reads.

Processing. De-duplicate by id (cursors overlap at page boundaries), parse createdAt (the format is %a %b %d %H:%M:%S %z %Y, always UTC), classify each post (repost if retweeted_tweet is present, else reply if isReply, else quote if quoted_tweet, else original), flag links, media and hashtags from entities and extendedEntities, and bucket every author by followers and account age rather than keeping the handle. Processing is where privacy is engineered in: the de-identified row that leaves this stage has no id, no text, no handle.

Analysis. Compute the aggregates the question asked for, and only those: hour-of-day histograms for both sets, type shares, concentration (what share of posts the repeat accounts write), bucket shares for the account population. Then compare them against something: the organization's own pattern, a second organization, a previous window. A number with no comparison is a description, not a finding.

Reporting. State the finding, the sample, the window, the confidence and, explicitly, what the sample cannot show. Report aggregates. If an individual account must be named for an operational reason, that is a route-two decision with its own authority, not a byproduct of the analysis. Decide how long the raw material is kept and delete it on schedule; GDPR's storage limitation principle applies even to public posts. The report then sets the next question, which is why the diagram above is a loop.

04 — Section

Three rules cover most route-one work on X, and all three are checkable against primary sources rather than vendor glossaries.

Rule 1: collect only what a logged-out visitor could see. Public posts, public profiles, public follow lists and public engagement counts are in scope. Direct messages are not: they are private communications and reading them without being a party is interception, not intelligence. Protected accounts are not: their posts are visible only to approved followers, and gaining that approval under a pretext is a HUMINT act with a deception problem. Access controls are the platform's statement of what is public; the open route respects them rather than working around them. On twitterapi.io, reads need an API key and no X account, which keeps the collection on the public surface by construction.

Rule 2: public is not the same as free of data-protection law. Under the EU General Data Protection Regulation, a public post that identifies a person is still personal data. Article 5(1)(b) requires that data be "collected for specified, explicit and legitimate purposes and not further processed in a manner that is incompatible with those purposes", and Article 5(1)(c) that it be "adequate, relevant and limited to what is necessary". A private organization typically relies on Article 6(1)(f), processing "necessary for the purposes of the legitimate interests pursued by the controller or by a third party", which requires a documented balancing test. Article 14 requires telling people whose data you obtained indirectly, unless doing so "proves to be impossible or would involve a disproportionate effort", which is the usual basis for aggregate research, and is one more reason to keep outputs aggregate. Article 9(2)(e) lifts the special-category ban only where "the data subject has manifestly made the personal data public", a narrower test than "it was on X". (Quotes from the regulation text on eur-lex.europa.eu, verified this session.)

Rule 3: for public authorities, systematic monitoring of a person is surveillance even on open sources. This is the Demos paper's own caveat: directed surveillance of open-source material is a route-two activity that requires authorization, if at a low level. In the UK the governing framework is Part II of the Regulation of Investigatory Powers Act 2000 for directed surveillance, with the Investigatory Powers Act 2016 covering interception and communications data (legislation.gov.uk/ukpga/2016/25). The Home Office's Covert Surveillance and Property Interference code of practice (2018) addresses online covert activity directly: it notes that individuals who post information on social media networks are less likely to hold a reasonable expectation of privacy in relation to that information, while treating repeated viewing of a particular person's open-source material as something that may amount to directed surveillance and require authorization. The practical reading for everyone else: a one-off aggregate study of a public conversation is research; returning daily to watch one named person is surveillance, and the law treats it as such regardless of the privacy setting.

What this looks like as a checklist on X.

ActionOpen route (this page)Why
Collect public posts mentioning an organizationyespublic surface, aggregate purpose
Collect an organization's public timelineyesorganization, not a private individual
Bucket mention authors by followers and ageyesa de-identified aggregate
Keep handles of private individuals in the outputnonot necessary for the question; Article 5(1)(c)
Name or rank the most active private accountsnoprofiling an individual; route two
Read direct messagesnointerception
Follow a protected account under a pretextnodeception; HUMINT with a legal problem
Return every day to watch one named personno (public authority) / document it (anyone else)directed surveillance territory

X's own developer and platform terms add a contractual layer on top of the statutory one; our Twitter OSINT page covers that layer, and it does not change any row above.

05 — Section

Worked investigation, steps 1 and 2: frame the question and collect

The question. Who drives the public conversation about a space agency on X, when does it happen relative to the agency's own posting, what kind of posts are they, and how concentrated is it in a few accounts? Unit of analysis: the post, with the account as a secondary unit for the concentration and bucket measures. Window: the most recent 500 mentions and the most recent 500 organization posts at collection time.

The sources. Two public surfaces: posts that mention @NASA (an organization account, so it can be named) and @NASA's own timeline. We did not collect profiles separately; the public author object embedded in each mention post carries the fields needed for bucketing, which kept the request count at 50 and meant no profile endpoint was called at all.

The collection plan, costed before the first call. 25 pages of /twitter/user/mentions at 20 posts a page is 500 posts and 7,500 credits; 25 pages of /twitter/user/last_tweets is the same again. Total: 50 requests, 1,000 posts, 15,000 credits, $0.15. At the official X API's pay-per-usage rate card (docs.x.com/x-api/getting-started/pricing, $0.005 per post read) the identical 1,000 reads are $5.00. The script below does the collection and then the processing and analysis steps; it is the complete study minus the charts. Raw mention pages were written to a temporary directory and not kept, because their authors are private individuals; the organization's own pages are kept compressed with the study.

Keyword triage, the step we did not need here. When the subject is a topic rather than an account, the same plan starts with /twitter/tweet/advanced_search (for example "artemis" lang:en -filter:replies since_time:1760000000, queryType=Latest) to find which accounts carry the topic, then moves to mentions or last_tweets for the accounts that matter. Search returns the same post shape, so the processing code does not change.

bash
# Collection plan for the worked exercise (cost it before the first call)
# 25 pages x 20 posts = 500 mentions, newest first; raw pages to a temp dir (private authors)
curl -s -H "X-API-Key: $TWITTERAPI_IO_API_KEY" \
  "https://api.twitterapi.io/twitter/user/mentions?userName=NASA" | jq '{n: (.tweets|length), has_next_page, next_cursor}'

# 25 pages x 20 posts = 500 of the organization's own posts (reposts included)
curl -s -H "X-API-Key: $TWITTERAPI_IO_API_KEY" \
  "https://api.twitterapi.io/twitter/user/last_tweets?userName=NASA" | jq '{n: (.data.tweets|length), has_next_page, next_cursor}'

# Topic triage when there is no obvious account: same post shape, so the same processing code
curl -s -G -H "X-API-Key: $TWITTERAPI_IO_API_KEY" \
  --data-urlencode 'query="artemis" lang:en -filter:replies since_time:1760000000' --data-urlencode 'queryType=Latest' \
  "https://api.twitterapi.io/twitter/tweet/advanced_search" | jq '.tweets | map(.author.userName) | group_by(.) | map({a: .[0], n: length}) | sort_by(-.n) | .[:10]'

# Budget: 50 requests x 20 posts x 15 credits = 15,000 credits = $0.15 ($1 = 100,000 credits)
06 — Section

Steps 3 and 4: process the posts and compare the two hour-of-day patterns

Processing produced two clean sets. 500 organization posts spanning 190.2 days (2.63 a day): 259 original posts (51.8%), 234 reposts (46.8%) and 7 quotes (1.4%); 41.6% carried media and 43.8% a link. 500 mention posts spanning just 30.1 hours (16.6 an hour), which is the first finding in itself: the public writes about this organization roughly six times faster than it posts, and a 500-post sample of mentions is a day and a quarter, not a season. Any claim about mention patterns below is a claim about that day and a quarter.

The hour-of-day comparison. Both histograms are in UTC. The organization is a working-day poster: 91.2% of its posts fall between 12:00 and 23:59 UTC, its three busiest hours are 18:00, 21:00 and 16:00 UTC, and the six hours from 03:00 to 08:59 UTC hold 1.6% of its posts. The public is flatter: 73.4% of mentions fall in the same 12-hour span, the busiest hours are 16:00, 14:00 and 17:00 UTC, and the overnight six hours hold 8.4%. Only one hour, 16:00 UTC, is in both top threes. The mention peak sits two hours before the organization's own peak, which is consistent with mentions following the morning news cycle and the previous day's posts rather than the organization's afternoon output, though a 30-hour window cannot prove that; it would take several weeks of windows and a lag correlation to say so with confidence.

Hour, UTCOrganization postsShare of organization postsMentionsShare of mentions
14:00428.4%5511.0%
16:005010.0%5711.4%
17:00479.4%489.6%
18:006412.8%163.2%
21:005611.2%326.4%
03:00 to 08:59 (six hours)81.6%428.4%
12:00 to 23:59 (twelve hours)45691.2%36773.4%

What this stage is for. In a real engagement the hour comparison answers an operational question: when should a communications team be watching, and when does a surge in mentions mean something has happened rather than that it is 16:00 UTC? The baseline is the thing of value. A posting-hour baseline costs about $0.08 per 500 posts to establish and the same to refresh, which is why it is the first SOCMINT product worth building.

Grouped bar chart of the share of posts by hour of day in UTC for two sets: @NASA's own 500 posts in blue and 500 public posts mentioning @NASA in orange. The organization's bars are near zero from 03:00 to 08:59 UTC, rise from 13:00, peak at 18:00 UTC with 64 posts or 12.8%, and have a second peak at 21:00 UTC. The mention bars are flatter, with a peak at 16:00 UTC of 57 posts or 11.4%, a near-equal bar at 14:00, a drop at 18:00 and 19:00, a second hump at 21:00 and a tail through the night. A shaded band marks 03:00 to 08:59 UTC with 1.6% of the organization's posts and 8.4% of mentions.
When the organization posts versus when the public mentions it, hour of day in UTC. 500 posts in each set, collected in 2026; shares are of each set. Only 16:00 UTC is in both top threes.
07 — Section

Step 5: what a mention is, and who sends it (aggregates, never profiles)

Type of mention. Of the 500 mention posts, 353 (70.6%) were replies, 95 (19.0%) original posts and 52 (10.4%) quotes; the mentions endpoint returned no reposts, so the repost share is unmeasured here. Only 10.0% of all mentions were replies to @NASA itself (inReplyToUsername); the other 60 points of reply share were people replying to one another in threads where the organization's handle was carried along. That is the normal shape of a conversation around a large organization: most of it is not addressed to the organization.

Content flags and languages. 14.6% of mentions carried a link, 18.0% media and 9.4% at least one hashtag. 68.4% were tagged English and 15.2% Spanish; 4.2% had the qme tag, which X uses for posts with media and no text. The hashtags that appeared three or more times were #venusretrograde (14), #nasa (7), #spacex (5), #stem (3) and #moon (3). The leading tag has nothing to do with the agency; it is a recurring astrology meme that tags space accounts, and it is a good illustration of why a hashtag count is a processing output rather than a finding until an analyst has looked at what the tag is.

Engagement. The median mention had 0 likes, 0 replies and 17 views; 73.0% had no likes at all. The median organization post had about 3,222 likes, 145 replies and 735,000 views. The two populations are not comparable on engagement, which is the point: a mention's existence is the signal, not its reach.

Concentration, the number that answers the question. The 500 mentions came from 303 distinct accounts. 60 of them (19.8%) mentioned the organization more than once in the window and between them wrote 257 posts, 51.4% of all mentions. The single most active account wrote 46, and the five most active together wrote 20.4%. Half of the public conversation about this organization in this window was a core of sixty accounts, and a tenth of it was five. We do not list them; the SOCMINT finding is the shape of the distribution, and anyone with an operational need to know who they are has a route-two decision to make and document.

Mentioning accounts (303 distinct)ShareCount
Fewer than 100 followers27.1%82
100 to 999 followers38.9%118
1,000 to 9,999 followers28.4%86
10,000 to 99,999 followers4.6%14
100,000 or more followers1.0%3
Account younger than 1 year12.9%39
Account 1 to 5 years old42.6%129
Account 5 to 10 years old13.9%42
Account 10 or more years old30.7%93
isBlueVerified (X Premium checkmark)29.7%90
isAutomated label0.3%1

Reading the buckets. The median mentioning account has 373 followers and is 4.5 years old; two thirds have fewer than 1,000 followers, and 5.6% have 10,000 or more. 29.7% carry the Premium checkmark, which on X is a paid subscription signal and not an identity check, so it says something about willingness to pay and nothing about whether the account is an organization. No field in the public profile says "organization"; the honest statement is that the mentioning population is overwhelmingly small, mostly established accounts, with a visible paid-subscriber minority and almost no accounts carrying X's automation label. Account-age and follower buckets are reach proxies, and the account-age distribution is the input for the account-age checks on our tools pages when a specific account must be assessed for a legitimate reason.

Two-panel chart. Left: a stacked horizontal bar showing what a mention of @NASA is across 500 posts: reply 70.6%, original 19.0%, quote 10.4%, with notes that only 10.0% are replies to @NASA itself, 14.6% carry a link, 18.0% media, 9.4% a hashtag, 73.0% have zero likes, and that 60 of 303 accounts mention more than once and write 51.4% of mentions, the single most active account 46 and the top 5 together 20.4%. Right: horizontal bars of the follower buckets of the 303 distinct mentioning accounts: under 100 followers 27.1% (82), 100 to 999 38.9% (118), 1k to 9,999 28.4% (86), 10k to 99,999 4.6% (14), 100k or more 1.0% (3), with a note of median 373 followers, median age 4.5 years, 29.7% Premium checkmark, 0.3% automation label.
Processing output for the worked exercise: what a mention is, and the follower buckets of the accounts behind the mentions. 500 posts collected in 2026; no individual account is identified.
08 — Section

What the exercise cost, and what the same reads cost on the official X API

A SOCMINT program is a recurring cost, so price per post read is the number to carry. twitterapi.io prices tweet endpoints (search, mentions, timelines, replies, quotes, tweets by ID) at 15 credits per post returned, $0.15 per 1,000 posts, with a 15-credit minimum per request; profiles are 18 credits each ($0.18 per 1,000) and follower or following lists 1 to 3 credits per user returned depending on page size. $1 is 100,000 credits and purchased credits do not expire. The official X API's pay-per-usage rate card is $0.005 per post read, $0.010 per user read and $0.010 per follower or following read, capped at 3 million post reads per monthly billing cycle with Enterprise above that; new accounts get $20 in credits on a first saved card and a match of the first auto-recharge up to $50.

Itemtwitterapi.ioOfficial X API (pay-per-usage)
This exercise: 1,000 posts, 50 requests$0.15$5.00
1,000 posts read$0.15 (15 credits each)$5.00 ($0.005 each), about 33x
A 500-post hour-of-day baseline, refreshed weekly for a yearabout $3.90about $130
1,000 public profiles read separately$0.18$10.00
100,000 followers of one account listed$1 to $3$1,000
Account needed for readsAPI key, no X accountX developer account with a saved card

The ratio is about 33x per 1,000 posts ($5.00 against $0.15), and for the follower graph it is larger, because X prices each follower record at a cent. Rate limits on twitterapi.io are 0.2 requests a second before a first payment, 20 after it, 50 on the Scale plan and 500 on Business, which for a study like this one is a time constraint rather than a cost one: the whole collection here was 50 requests. Monthly plans with bonus credits are on the pricing page; new accounts that pass the automatic bot check get up to $1 of free credits (at most 100,000 credits, no card, one per person or network, expiring after 31 days), which covers this exercise about six times over.

09 — Section

How we collected this

Question: for one public organization on X, when does the public mention it by hour of day compared with when it posts; what share of mentions are replies, quotes or original posts; what share carry links, media or hashtags; which hashtags recur; and how concentrated are mentions in a small set of accounts, with the account population described only in buckets.

Accounts and query: /twitter/user/mentions with userName=NASA, 25 pages (public posts mentioning the organization, newest first), and /twitter/user/last_tweets with userName=NASA, 25 pages (the organization's own timeline, reposts included, replies excluded by default). No profile, follower or search endpoint was called; author fields come from the public author object embedded in each mention post.

Window and sample: the 500 most recent mentions at collection time (30.1 hours) and the 500 most recent organization posts (190.2 days), collected in 2026 through a rate-limited client. 50 requests, 1,000 posts, 15,000 credits, $0.15. Posts were de-duplicated by id; times are UTC from createdAt; a post is a repost if retweeted_tweet is present, else a reply if isReply, else a quote if quoted_tweet is present, else original; hashtags appearing fewer than three times were dropped.

What the sample cannot show: the mention set covers a day and a quarter, so its hour-of-day pattern is one weekday's pattern and may not match a weekend or a launch day; the organization set covers six months and averages over events. The mentions endpoint returned no reposts, so repost share is unmeasured. Follower and account-age buckets are reach and tenure proxies; nothing in the public profile identifies an account as an organization, and the Premium checkmark is a subscription signal. The two-hour lead of the mention peak over the organization's peak is an observation from one window, not an established lag.

Privacy: aggregates only. @NASA is a public organization account and is named; the authors of mention posts are private individuals and are never listed, ranked, quoted or linked. Raw mention pages were kept in a temporary directory and not committed; the organization's own pages are kept gzip-compressed with the study, together with the protocol, the analysis script, the results file and a de-identified sample (hour, type, flags, follower bucket, age bucket, counts; no id, URL, text, author ID or handle).

python
"""socmint_exercise.py - a route-one SOCMINT exercise on X (Twitter) with twitterapi.io.

Collect public posts mentioning an organization and the organization's own posts, then
process them into aggregates: hour-of-day pattern for both sets, mention type shares,
concentration of repeat accounts, and follower / account-age buckets. No handle of a
private individual leaves this script.

    pip install requests
    export TWITTERAPI_IO_API_KEY=...        # twitterapi.io dashboard; no X account needed for reads
    python socmint_exercise.py NASA 25      # 25 pages x 20 = 500 posts per set, 50 requests, $0.15
"""
import json
import os
import sys
import time
from collections import Counter
from datetime import datetime, timezone

import requests

BASE = "https://api.twitterapi.io"
H = {"X-API-Key": os.environ["TWITTERAPI_IO_API_KEY"]}
FOLLOWER_BUCKETS = [("<100", 100), ("100-999", 1000), ("1k-9,999", 10000), ("10k-99,999", 100000), ("100k+", float("inf"))]
AGE_BUCKETS = [("<1 year", 1), ("1-5 years", 5), ("5-10 years", 10), ("10+ years", float("inf"))]


def get(url, params):
    for attempt in range(4):
        r = requests.get(url, headers=H, params=params, timeout=30)
        if r.status_code == 429:               # 0.2 req/s before a first payment, 20 after
            time.sleep(5 * (attempt + 1))
            continue
        r.raise_for_status()
        return r.json()
    raise RuntimeError("rate limited four times in a row")


def mentions(user_name, pages):
    """Public posts mentioning @user_name, 20 a page, newest first. Top-level tweets[]."""
    out, cursor = [], None
    for _ in range(pages):
        params = {"userName": user_name}
        if cursor:
            params["cursor"] = cursor
        body = get(f"{BASE}/twitter/user/mentions", params)
        out.extend(body.get("tweets") or [])
        if not body.get("has_next_page") or not body.get("next_cursor"):
            break
        cursor = body["next_cursor"]
    return out


def last_tweets(user_name, pages):
    """The account's own timeline, reposts included. Nested under data.tweets[]."""
    out, cursor = [], None
    for _ in range(pages):
        params = {"userName": user_name}
        if cursor:
            params["cursor"] = cursor
        body = get(f"{BASE}/twitter/user/last_tweets", params)
        out.extend((body.get("data") or {}).get("tweets") or [])
        if not body.get("has_next_page") or not body.get("next_cursor"):
            break
        cursor = body["next_cursor"]
    return out


def parse_time(s):
    return datetime.strptime(s, "%a %b %d %H:%M:%S %z %Y")   # always UTC


def classify(t):
    if t.get("retweeted_tweet"):
        return "repost"
    if t.get("isReply"):
        return "reply"
    if t.get("quoted_tweet"):
        return "quote"
    return "original"


def bucket(value, buckets):
    for label, upper in buckets:
        if value < upper:
            return label
    return buckets[-1][0]


def hour_hist(posts):
    h = [0] * 24
    for t in posts:
        h[parse_time(t["createdAt"]).hour] += 1
    return h


def analyze(org_posts, mention_posts, org_name):
    now = datetime.now(timezone.utc)
    org_posts = list({t["id"]: t for t in org_posts}.values())            # cursors overlap: dedupe
    mention_posts = list({t["id"]: t for t in mention_posts}.values())
    n = len(mention_posts)

    types = Counter(classify(t) for t in mention_posts)
    to_org = sum(1 for t in mention_posts if (t.get("inReplyToUsername") or "").lower() == org_name.lower())
    hashtags = Counter(h["text"].lower() for t in mention_posts
                       for h in (t.get("entities") or {}).get("hashtags") or [])

    # account population: bucket, never keep the handle
    per_author = Counter((t.get("author") or {}).get("id") for t in mention_posts)
    authors = {(t.get("author") or {}).get("id"): t["author"] for t in mention_posts if t.get("author")}
    followers = Counter(bucket(a.get("followers") or 0, FOLLOWER_BUCKETS) for a in authors.values())
    ages = Counter(bucket((now - parse_time(a["createdAt"])).days / 365.25, AGE_BUCKETS)
                   for a in authors.values() if a.get("createdAt"))
    repeat = [c for c in per_author.values() if c > 1]

    return {
        "org_posts": len(org_posts),
        "mentions": n,
        "mention_window_hours": round((max(parse_time(t["createdAt"]) for t in mention_posts)
                                       - min(parse_time(t["createdAt"]) for t in mention_posts)).total_seconds() / 3600, 1),
        "org_hour_utc": hour_hist(org_posts),
        "mention_hour_utc": hour_hist(mention_posts),
        "mention_types_pct": {k: round(100 * v / n, 1) for k, v in types.items()},
        "reply_to_org_pct": round(100 * to_org / n, 1),
        "with_url_pct": round(100 * sum(1 for t in mention_posts if (t.get("entities") or {}).get("urls")) / n, 1),
        "with_media_pct": round(100 * sum(1 for t in mention_posts if (t.get("extendedEntities") or {}).get("media")) / n, 1),
        "top_hashtags": [{"tag": f"#{k}", "n": v} for k, v in hashtags.most_common(10) if v >= 3],
        "distinct_accounts": len(authors),
        "repeat_accounts": len(repeat),
        "posts_by_repeat_accounts_pct": round(100 * sum(repeat) / n, 1),
        "max_posts_one_account": max(per_author.values()),
        "blue_verified_pct": round(100 * sum(1 for a in authors.values() if a.get("isBlueVerified")) / len(authors), 1),
        "follower_buckets": dict(followers),
        "age_buckets": dict(ages),
        "credits": 15 * (len(org_posts) + n),           # 15 credits per post returned; $1 = 100,000 credits
    }


if __name__ == "__main__":
    org = sys.argv[1] if len(sys.argv) > 1 else "NASA"
    pages = int(sys.argv[2]) if len(sys.argv) > 2 else 25
    m = mentions(org, pages)          # private authors: keep in memory or a temp dir, never commit raw pages
    o = last_tweets(org, pages)       # organization account: raw pages may be kept
    print(json.dumps(analyze(o, m, org), indent=2))
10 — Questions

Questions readers ask

What does SOCMINT stand for and who coined it?

SOCMINT is social media intelligence: intelligence derived from public social media activity through a cycle of question, collection, processing, analysis and reporting. The term was introduced in the Demos paper #Intelligence (April 2012) by Sir David Omand, Jamie Bartlett and Carl Miller, which writes "social media intelligence, which we term SOCMINT" and proposes six principles for its use plus a split between open, non-intrusive SOCMINT and intrusive surveillance.

What is the difference between SOCMINT and OSINT?

OSINT is intelligence from any lawfully available open source: web pages, public records, media, data sets. SOCMINT is the subset whose source is social platforms: posts, profiles, the follow graph, engagement and timing. The narrower source changes the method, because a SOCMINT exercise classifies thousands of posts and reports aggregates, where an OSINT desk reads and cross-references documents. Neither elicits information from a person (HUMINT) nor intercepts communications (SIGINT).

Is SOCMINT legal?

Collecting and analyzing public posts for a stated, legitimate purpose is lawful in most jurisdictions, but public does not mean unregulated. Under GDPR a public post is still personal data: you need a purpose (Article 5(1)(b)), minimal collection (Article 5(1)(c)), a legal basis such as legitimate interests (Article 6(1)(f)) and a position on Article 14 notice. Reading direct messages, working around access controls or deceiving a protected account into accepting you is not SOCMINT and may be a crime. For public authorities, repeated monitoring of a named person is directed surveillance under UK law even on open sources.

What data can SOCMINT collect from X (Twitter) without an X account?

Everything a logged-out visitor can see, read by machine: public posts with their text, timestamp, language, reply and quote relationships, entities and media; public profiles with follower counts, creation date and the Premium and automation flags; public follower and following lists; search results by operator. On twitterapi.io these are read with an API key and no X account. Direct messages and protected accounts are outside the open route.

How many posts does a SOCMINT exercise need?

It depends on the question. A posting-hour pattern is readable from a few hundred posts; a share estimate such as "70.6% of mentions are replies" at a few points' precision needs about five hundred; a trend over months needs repeated windows. Our worked exercise used 500 mentions and 500 organization posts, 50 requests and $0.15, and the write-up states what that sample cannot show: a 30-hour mention window is one weekday, not a season.

What does a SOCMINT study cost on twitterapi.io versus the official X API?

Tweet endpoints on twitterapi.io are 15 credits per post returned, $0.15 per 1,000 posts, with a 15-credit minimum per request; the official X API pay-per-usage rate card is $0.005 per post read, $5.00 per 1,000, about 33x more, capped at 3 million post reads a month. The 1,000-post exercise on this page cost $0.15 against $5.00. New accounts that pass the bot check get up to $1 of free credits (at most 100,000 credits, no card, one per person or network, expiring after 31 days).

Can SOCMINT identify which accounts are organizations or bots?

Not from a single public field. X's public profile carries isBlueVerified (a paid Premium subscription, not an identity check), isAutomated (a self-declared automation label, 0.3% of the accounts in our sample) and follower, following and creation-date counts, which are reach and tenure proxies. In the worked exercise 29.7% of mentioning accounts carried the Premium checkmark and two thirds had under 1,000 followers. Treat those as buckets that describe a population, and reserve any judgment about a specific account for a documented, legitimate need.

11 — Further reading

Continue

Sources & further reading
More from this series
Build it

Stop reading. Start building.

Starter credits cover real testing on real data. Google sign-in, no card, no application queue.

Get an API key