{"openapi": "3.1.0", "info": {"title": "TwitterScore API", "version": "1.0.0", "summary": "Twitter Score, Smart Followers and crypto-Twitter analytics for any X account.", "description": "The TwitterScore API returns the same data as twitterscore.io: a 0–1000 **Twitter Score** for\ncrypto X (Twitter) accounts (follower quality, not follower count), account info, score and follower\nhistory, Smart Followers, trending and Smart Follows lists.\n\n**Authentication.** Send your API key in the `X-API-Key` header (preferred), as\n`Authorization: Bearer <key>`, or as the `api_key` query parameter (the original form; it keeps\nworking, but keys in URLs end up in logs and referers). Keys are issued on\nhttps://twitterscore.io/api-dashboard/ after choosing a plan on https://twitterscore.io/api/prices.\n\n**Errors.** Every error body is `{\"success\": false, \"message\": \"...\", \"error\": {\"code\", \"message\",\n\"docs_url\"}}`. During the compatibility period errors are returned with HTTP 200 (clients built on the\nold API branch on the body); the real statuses (401, 403, 404, 400, 405, 429, 500) are enabled\nper the published changelog.\n\n**Limits.** A per-minute rate and a monthly quota depend on the plan; `GET /api/v1/limits` returns\nthe remaining quota. Exceeding a limit returns 429 with `Retry-After` (per-minute) or the quota\nmessage (monthly). Successful responses are cached server-side for a short time per query.\n\n**Data freshness.** Scores, ranks and Smart Followers are recalculated daily. Cite data as\n\"TwitterScore (twitterscore.io)\" with the date of the response.\n", "termsOfService": "https://twitterscore.io/privacy-policy/", "contact": {"name": "TwitterScore", "url": "https://twitterscore.io/contacts/", "email": "team@twitterscore.io"}, "x-pricing": "https://twitterscore.io/api/prices", "x-documentation": "https://twitterscore.io/developers/", "x-llms-txt": "https://twitterscore.io/llms.txt"}, "servers": [{"url": "https://twitterscore.io", "description": "Production"}], "tags": [{"name": "Scores", "description": "Twitter Score of one or many accounts and its history."}, {"name": "Accounts", "description": "Account profile data."}, {"name": "Followers", "description": "Smart Followers, follower statistics and categories."}, {"name": "Mentions", "description": "Smart Mentions and mentioners."}, {"name": "Lists", "description": "Trending, Smart Follows, Top Researched and other ranked lists."}, {"name": "History", "description": "Follow/unfollow history between accounts."}, {"name": "Reference", "description": "Categories, tags and account limits."}], "security": [{"ApiKeyHeader": []}, {"BearerKey": []}, {"ApiKeyQuery": []}], "paths": {"/api/v1/bulk_scores_check": {"get": {"operationId": "bulk_scores_check", "summary": "Twitter Scores for up to 50 accounts in one request", "description": "Looks up many accounts at once from a comma-separated list of `ids` (numeric X/Twitter ids) or `usernames` (handles) and returns each one's Twitter Score with a link to the profile. `ids` wins when both are non-empty. Only the first 50 comma-separated entries are considered (extra entries are silently dropped; the cap is applied before validation, so non-numeric entries still consume slots) and non-numeric ids are skipped. Accounts that TwitterScore does not know are simply absent from the result — there is no per-item error — so `total` is the number of MATCHED accounts, not the number requested. Matched rows keep the order of the input list and are paginated with `page`/`size` (size default 10, max 50).\n\n**Required:** one of `ids`, `usernames`.\n**Nested fields**\n\n- `data[].twitter_id` (string): Numeric X/Twitter id as a string (CONVERT(..., CHAR) in SQL).\n- `data[].username` (string): Handle as stored by TwitterScore (canonical casing).\n- `data[].twitter_url` (string): Profile link, \"https://x.com/<username>\".\n- `data[].twitter_score` (number): Twitter Score 0–1000; stored as DOUBLE, emitted as a float (e.g. 1000.0).", "tags": ["Scores"], "security": [{"ApiKeyHeader": []}, {"BearerKey": []}, {"ApiKeyQuery": []}], "parameters": [{"name": "ids", "in": "query", "required": false, "description": "Comma-separated numeric X/Twitter ids, at most 50 (the rest are ignored). Non-numeric entries are dropped; if none remain the call still succeeds with total 0. Takes precedence over `usernames` whenever non-empty.", "schema": {"type": "string"}, "example": "295218901,44196397"}, {"name": "usernames", "in": "query", "required": false, "description": "Comma-separated handles without @, at most 50; surrounding whitespace per entry is stripped. Used only when `ids` is empty.", "schema": {"type": "string"}, "example": "VitalikButerin,elonmusk"}, {"name": "page", "in": "query", "required": false, "description": "1-based page of the matched rows. Non-integer or 0 falls back to 1; negative values are floored to 1. A page past the end returns an empty `data` with the same total/pages.", "schema": {"type": "integer", "default": 1}}, {"name": "size", "in": "query", "required": false, "description": "Rows per page; values above 50 are capped to 50, non-integer values fall back to 10, values below 1 are floored to 1.", "schema": {"type": "integer", "default": 10, "maximum": 50}}], "responses": {"200": {"description": "Success", "content": {"application/json": {"schema": {"type": "object", "properties": {"success": {"type": "boolean", "description": "true on success; false with a `message` when neither `ids` nor `usernames` was given."}, "total": {"type": "integer", "description": "Number of accounts matched across all pages (unknown accounts are not counted)."}, "page": {"type": "integer", "description": "Page returned (≥1)."}, "size": {"type": "integer", "description": "Number of items in `data` on this page."}, "pages": {"type": "integer", "description": "Total number of pages (at least 1, even when total is 0)."}, "data": {"type": "array", "description": "Matched accounts in the order of the input list."}}}, "example": {"success": true, "total": 2, "page": 1, "size": 2, "pages": 1, "data": [{"twitter_id": "295218901", "username": "VitalikButerin", "twitter_url": "https://x.com/VitalikButerin", "twitter_score": 1000.0}, {"twitter_id": "44196397", "username": "elonmusk", "twitter_url": "https://x.com/elonmusk", "twitter_score": 1000.0}]}}}}, "default": {"$ref": "#/components/responses/Error"}}, "x-error-codes": ["api_key_missing", "api_key_invalid", "api_access_deactivated", "api_key_revoked", "invalid_params", "method_not_allowed", "rate_limited", "quota_exceeded", "server_error"], "x-one-of-required": [["ids", "usernames"]]}}, "/api/v1/followers_count_history": {"get": {"operationId": "followers_count_history", "summary": "Daily follower-count snapshots for an account over a period", "description": "Returns the account's follower count per day for the chosen period, newest day first, paginated. Each item is one daily snapshot taken when TwitterScore scanned the account's audience; days without a scan are skipped, so total reflects the number of snapshots actually stored, not the number of calendar days in the period. Use period=all together with page to walk the full history. Only accounts that TwitterScore monitors have snapshots: for a profile known only as a follower (not monitored) the list is empty with total 0.\n\n**Required:** one of `username`, `twitter_id`.\n**Nested fields**\n\n- `followers_count[].date` (string): Snapshot date in YYYY-MM-DD.\n- `followers_count[].followers_count` (integer): Follower count recorded on that date.", "tags": ["Followers", "History"], "security": [{"ApiKeyHeader": []}, {"BearerKey": []}, {"ApiKeyQuery": []}], "parameters": [{"name": "username", "in": "query", "required": false, "description": "X/Twitter handle of the account, without the leading @. Surrounding whitespace and a trailing slash are tolerated; renamed handles are resolved via previous-username history.", "schema": {"type": "string"}, "example": "VitalikButerin"}, {"name": "twitter_id", "in": "query", "required": false, "description": "Numeric X/Twitter user id of the account. Tried first when both are sent; falls back to username if the id resolves nothing.", "schema": {"type": "integer"}, "example": 295218901}, {"name": "period", "in": "query", "required": false, "description": "Look-back window, case-insensitive. 3d=3 days, 7d/1w=7 days, 30d/1m=30 days, 180d/6m=180 days, 365d/1y=365 days, all=entire stored history. The window covers today and the previous N-1 days (N calendar days inclusive). Any unrecognised value (including 360d mentioned in the public docs) silently falls back to 30 days.", "schema": {"type": "string", "enum": ["3d", "7d", "1w", "30d", "1m", "180d", "6m", "365d", "1y", "all"], "default": "30d"}, "example": "7d"}, {"name": "page", "in": "query", "required": false, "description": "1-based page number. Non-numeric, 0 or negative values are treated as 1. A page beyond the last returns an empty list with the same metadata.", "schema": {"type": "integer", "default": 1}, "example": 1}, {"name": "size", "in": "query", "required": false, "description": "Snapshots per page, capped at 30. Non-numeric values fall back to 30; 0 or negative values are floored to 1.", "schema": {"type": "integer", "default": 30, "maximum": 30}, "example": 30}], "responses": {"200": {"description": "Success", "content": {"application/json": {"schema": {"type": "object", "properties": {"success": {"type": "boolean", "description": "true on success."}, "total": {"type": "integer", "description": "Number of daily snapshots stored within the period (not the number of calendar days)."}, "page": {"type": "integer", "description": "The page that was served."}, "size": {"type": "integer", "description": "Number of snapshots actually returned on this page."}, "pages": {"type": "integer", "description": "Total pages for the requested size; at least 1."}, "followers_count": {"type": "array", "description": "Snapshots for this page, newest date first."}}}, "example": {"success": true, "total": 3, "page": 1, "size": 3, "pages": 1, "followers_count": [{"date": "2026-10-03", "followers_count": 5812344}, {"date": "2026-10-02", "followers_count": 5811902}, {"date": "2026-10-01", "followers_count": 5811475}]}}}}, "default": {"$ref": "#/components/responses/Error"}}, "x-error-codes": ["api_key_missing", "api_key_invalid", "api_access_deactivated", "api_key_revoked", "invalid_params", "account_not_found", "method_not_allowed", "rate_limited", "quota_exceeded", "server_error"], "x-one-of-required": [["username", "twitter_id"]]}}, "/api/v1/friendshipHistory/followed": {"get": {"operationId": "friendshipHistory_followed", "summary": "Monitored accounts that followed or unfollowed a given account (follow/unfollow log, incoming)", "description": "Returns the incoming follow activity of one account: every recorded event where a TwitterScore-monitored account followed (`action: \"Followed\"`) or unfollowed (`action: \"Unfollowed\"`) it, newest first, with the follower's profile summary, Twitter Score, follower count, tags, categories and the event date. Identify the account by `username` or `twitter_id`. Only followers that are active, non-suspended accounts under TwitterScore monitoring are included — this is the \"who in the crypto graph followed X\" view, not a complete follower log. Narrow to one day with `date` (YYYY-MM-DD), or to followers carrying given tags / categories with comma-separated `tag_ids` / `category_ids` (ids from get_tags_list / get_categories_list). Pages hold at most 25 rows.\n\n**Required:** one of `username`, `twitter_id`.\n**Nested fields**\n\n- `data[].action` (string): \"Followed\" when the follower followed the account, \"Unfollowed\" when it unfollowed.\n- `data[].twitter_id` (string): Follower's numeric Twitter/X id as a string.\n- `data[].username` (string): Follower's handle.\n- `data[].name` (string): Follower's display name.\n- `data[].twitter_score` (number): Follower's current Twitter Score (0-1000 scale) as a float (Account.project_score FloatField, may have decimals).\n- `data[].followers_count` (integer): Follower's current follower count (current, not historical).\n- `data[].description` (string): Follower's bio (empty string when none).\n- `data[].profile_image` (string): Follower's avatar URL from the stored S3 profile image (https://<bucket>.s3.amazonaws.com/profiles/<file>), or null when no image is stored.\n- `data[].tags` (array): Tags of the follower; empty array when none.\n- `data[].tags[].id` (integer): Tag id.\n- `data[].tags[].name` (string): Tag name.\n- `data[].categories` (array): Account categories of the follower; empty array when none.\n- `data[].categories[].id` (integer): Category id.\n- `data[].categories[].name` (string): Category name.\n- `data[].created_at` (string): Date of the follow/unfollow event, YYYY-MM-DD.", "tags": ["History"], "security": [{"ApiKeyHeader": []}, {"BearerKey": []}, {"ApiKeyQuery": []}], "parameters": [{"name": "username", "in": "query", "required": false, "description": "Handle of the account whose incoming follows to list (surrounding whitespace and trailing slash tolerated). Resolved against current usernames/slugs first, then against previous usernames (renamed accounts). Required unless `twitter_id` is given.", "schema": {"type": "string"}, "example": "VitalikButerin"}, {"name": "twitter_id", "in": "query", "required": false, "description": "Numeric Twitter/X id of the account. Takes precedence over `username` when both are given. Required unless `username` is given.", "schema": {"type": "integer"}, "example": 295218901}, {"name": "date", "in": "query", "required": false, "description": "Only events on this calendar day, format YYYY-MM-DD (date part of the recorded action timestamp). Any other format returns {\"success\": false, \"message\": \"Invalid date format. Use YYYY-MM-DD.\"}.", "schema": {"type": "string"}, "example": "2026-10-01"}, {"name": "tag_ids", "in": "query", "required": false, "description": "Comma-separated tag ids (see /api/v1/get_tags_list); only events whose follower carries at least one of these tags are returned. If any element is non-numeric the whole list is ignored (no filter); empty elements are skipped. A follower matching several of the ids can appear as duplicate rows (no DISTINCT).", "schema": {"type": "string"}, "example": "4,17"}, {"name": "category_ids", "in": "query", "required": false, "description": "Comma-separated account-category ids (see /api/v1/get_categories_list); only events whose follower is in at least one of these categories are returned. A list with a non-numeric element is ignored. Same duplicate-row caveat as tag_ids.", "schema": {"type": "string"}, "example": "2"}, {"name": "page", "in": "query", "required": false, "description": "1-based page number. Non-numeric, 0 or negative values are treated as 1; a page past the last returns an empty `data` array.", "schema": {"type": "integer", "default": 1}, "example": 1}, {"name": "size", "in": "query", "required": false, "description": "Rows per page, capped at 25. Values below 1 are treated as 1; a non-numeric value falls back to 10.", "schema": {"type": "integer", "default": 10, "maximum": 25}, "example": 10}], "responses": {"200": {"description": "Success", "content": {"application/json": {"schema": {"type": "object", "properties": {"success": {"type": "boolean", "description": "true for a served page."}, "total": {"type": "integer", "description": "Total number of follow/unfollow events matching the filters (may be inflated by duplicate rows when several tag_ids/category_ids match one follower)."}, "page": {"type": "integer", "description": "Served page number."}, "size": {"type": "integer", "description": "Number of items returned in `data`."}, "pages": {"type": "integer", "description": "Total pages at the requested size; at least 1."}}}, "example": {"success": true, "total": 2318, "page": 1, "size": 2, "pages": 1159, "data": [{"action": "Followed", "twitter_id": "1138033434", "username": "paradigm", "name": "Paradigm", "twitter_score": 884.21, "followers_count": 251340, "description": "Research-driven crypto investment firm.", "profile_image": "https://twitterscore.s3.amazonaws.com/profiles/paradigm.jpg", "tags": [{"id": 4, "name": "VC"}], "categories": [{"id": 2, "name": "Venture Capitals"}], "created_at": "2026-10-01"}, {"action": "Unfollowed", "twitter_id": "44196397", "username": "elonmusk", "name": "Elon Musk", "twitter_score": 1000.0, "followers_count": 221504311, "description": "", "profile_image": null, "tags": [], "categories": [{"id": 3, "name": "Influencers"}], "created_at": "2026-09-30"}]}}}}, "default": {"$ref": "#/components/responses/Error"}}, "x-error-codes": ["api_key_missing", "api_key_invalid", "api_access_deactivated", "api_key_revoked", "invalid_params", "account_not_found", "method_not_allowed", "rate_limited", "quota_exceeded", "server_error"], "x-one-of-required": [["username", "twitter_id"]]}}, "/api/v1/friendshipHistory/following": {"get": {"operationId": "friendshipHistory_following", "summary": "Accounts a given account started or stopped following (follow/unfollow log, outgoing)", "description": "Returns the outgoing follow activity of one account: every recorded event where the account followed (`action: \"Following\"`) or unfollowed (`action: \"Unfollowing\"`) another account, with the followed account's profile summary, Twitter Score, follower count, tags, categories and the event date. Identify the account by `username` or `twitter_id`. Only followed accounts that TwitterScore also tracks as full accounts are included (the query inner-joins the main accounts table), and the tags/categories shown are those of the followed account. Narrow the log to one day with `date` (YYYY-MM-DD), or to followed accounts carrying given tags / categories with comma-separated `tag_ids` / `category_ids` (ids from get_tags_list / get_categories_list). The whole history is loaded and then paginated in pages of at most 25. Rows are grouped and come back ordered by action name (\"Following\" before \"Unfollowing\") and then by twitter_id as a string, not newest-first; use `date` to look at a specific day.\n\n**Required:** one of `username`, `twitter_id`.\n**Nested fields**\n\n- `data[].action` (string): \"Following\" when the account followed the target, \"Unfollowing\" when it unfollowed.\n- `data[].twitter_id` (string): Followed account's numeric Twitter/X id as a string (CONVERT ... CHAR).\n- `data[].username` (string): Followed account's handle.\n- `data[].name` (string): Followed account's display name. Events whose followed account has no name are dropped by the grouping step (see notes), so this is never null in practice.\n- `data[].twitter_score` (number): Followed account's Twitter Score (0-1000 scale) as a float; the main-account score is used, falling back to the graph-side score when the main one is 0.\n- `data[].followers_count` (integer): Followed account's follower count at the time of serving (current, not historical).\n- `data[].description` (string): Followed account's bio.\n- `data[].profile_image_url` (string): Followed account's avatar URL on TwitterScore S3 (https://twitterscore.s3.amazonaws.com/<stored path>), or \"https://twitterscore.io/media/profiles/NoImageFound.png\" when none (both literals are in the SQL).\n- `data[].created_at` (string): Date of the follow/unfollow event, YYYY-MM-DD.\n- `data[].tags` (array): Tags of the followed account, de-duplicated; empty array when none.\n- `data[].tags[].id` (number): Tag id. Integral, but may be serialised as e.g. 4.0 when the result set contains untagged rows (pandas NULL promotion; see notes).\n- `data[].tags[].name` (string): Tag name.\n- `data[].categories` (array): Account categories of the followed account, de-duplicated; empty array when none.\n- `data[].categories[].id` (number): Category id (same float caveat as tags[].id).\n- `data[].categories[].name` (string): Category name.", "tags": ["History"], "security": [{"ApiKeyHeader": []}, {"BearerKey": []}, {"ApiKeyQuery": []}], "parameters": [{"name": "username", "in": "query", "required": false, "description": "Handle of the account whose outgoing follows to list (surrounding whitespace and trailing slash tolerated). Resolved against current usernames/slugs first, then against previous usernames (renamed accounts). Required unless `twitter_id` is given.", "schema": {"type": "string"}, "example": "VitalikButerin"}, {"name": "twitter_id", "in": "query", "required": false, "description": "Numeric Twitter/X id of the account. Takes precedence over `username` when both are given. Required unless `username` is given.", "schema": {"type": "integer"}, "example": 295218901}, {"name": "date", "in": "query", "required": false, "description": "Only events on this calendar day, format YYYY-MM-DD (DATE() of the recorded action timestamp). Any other format returns {\"success\": false, \"message\": \"Invalid date format. Use YYYY-MM-DD.\"}.", "schema": {"type": "string"}, "example": "2026-09-28"}, {"name": "tag_ids", "in": "query", "required": false, "description": "Comma-separated tag ids (see /api/v1/get_tags_list); only events whose followed account carries at least one of these tags are returned (followed accounts with no tag are excluded while this filter is set). If any element is non-numeric the whole list is ignored (no filter); empty elements are skipped.", "schema": {"type": "string"}, "example": "4,17"}, {"name": "category_ids", "in": "query", "required": false, "description": "Comma-separated account-category ids (see /api/v1/get_categories_list); only events whose followed account is in at least one of these categories are returned (uncategorised followed accounts are excluded while set). A list with a non-numeric element is ignored.", "schema": {"type": "string"}, "example": "2"}, {"name": "page", "in": "query", "required": false, "description": "1-based page number. Non-numeric, 0 or negative values are treated as 1; a page past the last returns an empty `data` array.", "schema": {"type": "integer", "default": 1}, "example": 1}, {"name": "size", "in": "query", "required": false, "description": "Rows per page, capped at 25. Values below 1 are treated as 1; a non-numeric value falls back to 10.", "schema": {"type": "integer", "default": 10, "maximum": 25}, "example": 10}], "responses": {"200": {"description": "Success", "content": {"application/json": {"schema": {"type": "object", "properties": {"success": {"type": "boolean", "description": "true for a served page."}, "total": {"type": "integer", "description": "Total number of follow/unfollow events matching the filters (after grouping)."}, "page": {"type": "integer", "description": "Served page number."}, "size": {"type": "integer", "description": "Number of items returned in `data`."}, "pages": {"type": "integer", "description": "Total pages at the requested size; at least 1."}}}, "example": {"success": true, "total": 143, "page": 1, "size": 2, "pages": 72, "data": [{"action": "Following", "twitter_id": "1138033434", "username": "paradigm", "name": "Paradigm", "twitter_score": 884.0, "followers_count": 251340, "description": "Research-driven crypto investment firm.", "profile_image_url": "https://twitterscore.s3.amazonaws.com/profiles/paradigm.jpg", "created_at": "2026-09-28", "tags": [{"id": 4, "name": "VC"}], "categories": [{"id": 2, "name": "Venture Capitals"}]}, {"action": "Following", "twitter_id": "2312333412", "username": "ethereum", "name": "Ethereum", "twitter_score": 996.0, "followers_count": 3821004, "description": "Ethereum is a global, open-source platform for decentralized applications.", "profile_image_url": "https://twitterscore.io/media/profiles/NoImageFound.png", "created_at": "2026-09-12", "tags": [], "categories": [{"id": 1, "name": "Projects"}]}]}}}}, "default": {"$ref": "#/components/responses/Error"}}, "x-error-codes": ["api_key_missing", "api_key_invalid", "api_access_deactivated", "api_key_revoked", "invalid_params", "account_not_found", "method_not_allowed", "rate_limited", "quota_exceeded", "server_error"], "x-one-of-required": [["username", "twitter_id"]]}}, "/api/v1/get_alpha": {"get": {"operationId": "get_alpha", "summary": "Legacy alias of get_smart_follows (Smart Follows / former Alpha ranking)", "description": "Identical to /api/v1/get_smart_follows: same parameters, same response. The web page was renamed from \"Alpha\" to \"Smart Follows\"; this path is kept working so existing integrations do not break, but new integrations should call /api/v1/get_smart_follows. The two paths are separate wrappers over one implementation so usage analytics can track the migration; they have separate per-minute rate-limit buckets (`api-get-alpha` vs `api-get-smart-follows`) and separate server-side response caches. See get_smart_follows for the full field reference.\n**Nested fields**\n\n- `data[]` (array): Same item shape as /api/v1/get_smart_follows — see that endpoint.", "tags": ["Lists"], "security": [{"ApiKeyHeader": []}, {"BearerKey": []}, {"ApiKeyQuery": []}], "parameters": [{"name": "days", "in": "query", "required": false, "description": "Period window in days: 1, 7 or 30. Any other integer returns {\"success\": false, \"message\": \"Days param is only available: 1|7|30\"}; a non-numeric value falls back to 7.", "schema": {"type": "integer", "enum": ["1", "7", "30"], "default": 7}, "example": 7}, {"name": "by", "in": "query", "required": false, "description": "Ranking column: `alpha` = change in new smart followers versus the previous period (new_diff), `score` = current Twitter Score, `followers` = current follower count. Case-sensitive; any other value returns {\"success\": false, \"message\": \"Sorting is only possible by [score|followers|alpha]\"}.", "schema": {"type": "string", "enum": ["alpha", "score", "followers"], "default": "alpha"}, "example": "alpha"}, {"name": "sort", "in": "query", "required": false, "description": "Sort direction: `asc` or `ascending` (case-insensitive) for ascending, anything else for descending.", "schema": {"type": "string", "enum": ["desc", "asc", "descending", "ascending"], "default": "desc"}, "example": "desc"}, {"name": "bucket", "in": "query", "required": false, "description": "Which set of smart followers to count: `all`, `vc` (venture capitals), `inf` (influencers) or `angels`. Case-insensitive; unknown values silently fall back to `all`.", "schema": {"type": "string", "enum": ["all", "vc", "inf", "angels"], "default": "all"}, "example": "vc"}, {"name": "page", "in": "query", "required": false, "description": "1-based page number; non-numeric, 0 or negative values are treated as 1.", "schema": {"type": "integer", "default": 1}, "example": 1}, {"name": "size", "in": "query", "required": false, "description": "Rows per page, capped at 100; values below 1 are treated as 1, non-numeric falls back to 10.", "schema": {"type": "integer", "default": 10, "maximum": 100}, "example": 10}], "responses": {"200": {"description": "Success", "content": {"application/json": {"schema": {"type": "object", "properties": {"success": {"type": "boolean", "description": "true for a served page."}, "total": {"type": "integer", "description": "Ranked rows available for this days/bucket/sort combination; at most 1000."}, "page": {"type": "integer", "description": "Served page number."}, "size": {"type": "integer", "description": "Number of items returned in `data`."}, "pages": {"type": "integer", "description": "Total pages at the requested size; at least 1."}, "days": {"type": "integer", "description": "Applied window (1, 7 or 30)."}, "bucket": {"type": "string", "description": "Applied (resolved) bucket: all, vc, inf or angels."}}}, "example": {"success": true, "total": 1000, "page": 1, "size": 1, "pages": 1000, "days": 7, "bucket": "all", "data": [{"twitter_id": "295218901", "username": "VitalikButerin", "name": "vitalik.eth", "profile_url": "/twitter/VitalikButerin", "profile_image": "https://twitterscore.s3.amazonaws.com/profiles/VitalikButerin.jpg", "blue_verified": true, "description": "mi pinxe lo crino tcati", "twitter_score": 1000, "twitter_score_diff": 0, "current_followers": 5812345, "followers_diff": 14135, "total_alpha_followers": 2417, "new_alpha_followers_diff": 36, "top_alpha_followers": [{"name": "Balaji", "username": "balajis", "image": "https://twitterscore.s3.amazonaws.com/profiles/balajis.jpg", "score": 942, "verified": true}]}]}}}}, "default": {"$ref": "#/components/responses/Error"}}, "x-error-codes": ["api_key_missing", "api_key_invalid", "api_access_deactivated", "api_key_revoked", "method_not_allowed", "rate_limited", "quota_exceeded", "server_error"], "deprecated": true}}, "/api/v1/get_categories": {"get": {"operationId": "get_categories", "summary": "List of account categories", "description": "Returns TwitterScore's account categories — the coarse classification of an account (Venture Capitals, Founders, Projects, Exchanges, Auditors, Media, Influencers, Angels) — ordered by id. These ids are the `category_id` values returned by get_twitter_info. The list is small and static; it is paginated with `page`/`size` (default 10, max 25). There are no filter parameters.\n**Nested fields**\n\n- `categories[].id` (integer): Category id (matches `category_id` in get_twitter_info).\n- `categories[].name` (string): Category name.", "tags": ["Reference"], "security": [{"ApiKeyHeader": []}, {"BearerKey": []}, {"ApiKeyQuery": []}], "parameters": [{"name": "page", "in": "query", "required": false, "description": "1-based page number. Non-integer or 0 falls back to 1, negatives are floored to 1; a page past the end returns an empty list with the same total/pages.", "schema": {"type": "integer", "default": 1}}, {"name": "size", "in": "query", "required": false, "description": "Items per page; values above 25 are capped to 25, non-integer values fall back to 10, values below 1 are floored to 1.", "schema": {"type": "integer", "default": 10, "maximum": 25}}], "responses": {"200": {"description": "Success", "content": {"application/json": {"schema": {"type": "object", "properties": {"success": {"type": "boolean", "description": "Always true (an internal SQL failure yields an empty list, not an error)."}, "total": {"type": "integer", "description": "Total number of categories."}, "page": {"type": "integer", "description": "Page returned (≥1)."}, "size": {"type": "integer", "description": "Number of items in `categories` on this page."}, "pages": {"type": "integer", "description": "Total number of pages (at least 1)."}, "categories": {"type": "array", "description": "Categories ordered by id."}}}, "example": {"success": true, "total": 8, "page": 1, "size": 8, "pages": 1, "categories": [{"id": 1, "name": "Venture Capitals"}, {"id": 2, "name": "Founders"}, {"id": 3, "name": "Projects"}, {"id": 4, "name": "Exchanges"}, {"id": 5, "name": "Auditors"}, {"id": 7, "name": "Media"}, {"id": 8, "name": "Influencers"}, {"id": 9, "name": "Angels"}]}}}}, "default": {"$ref": "#/components/responses/Error"}}, "x-error-codes": ["api_key_missing", "api_key_invalid", "api_access_deactivated", "api_key_revoked", "method_not_allowed", "rate_limited", "quota_exceeded", "server_error"]}}, "/api/v1/get_categorized_followers_count": {"get": {"operationId": "get_categorized_followers_count", "summary": "Number of monitored followers per account category", "description": "Breaks the account's monitored followers down by TwitterScore category (Projects, Venture Capitals, Influencers, Founders, and so on). The first entry, named \"All\", is the distinct count of monitored followers; the last entry, \"NoCategory\", counts followers that have no category. Entries in between are ordered Projects, Venture Capitals, Influencers first, then any other categories by count descending. A follower that belongs to several categories is counted in each of them, so per-category counts can add up to more than \"All\". Only active monitored followers with an active follow relationship are counted, which is why the numbers are far below the raw X follower count.\n\n**Required:** one of `username`, `twitter_id`.\n**Nested fields**\n\n- `categories[].name` (string): Category name, or the synthetic names \"All\" and \"NoCategory\".\n- `categories[].id` (integer): Category id as used by get_categories and get_followers filters. null for \"All\", 0 for \"NoCategory\".\n- `categories[].cnt` (integer): Number of monitored followers in this category. For \"All\" it is the distinct follower count.", "tags": ["Followers"], "security": [{"ApiKeyHeader": []}, {"BearerKey": []}, {"ApiKeyQuery": []}], "parameters": [{"name": "username", "in": "query", "required": false, "description": "X/Twitter handle of the account, without the leading @. Surrounding whitespace and a trailing slash are tolerated; renamed handles are resolved via previous-username history.", "schema": {"type": "string"}, "example": "VitalikButerin"}, {"name": "twitter_id", "in": "query", "required": false, "description": "Numeric X/Twitter user id of the account. Tried first when both are sent; falls back to username if the id resolves nothing.", "schema": {"type": "integer"}, "example": 295218901}], "responses": {"200": {"description": "Success", "content": {"application/json": {"schema": {"type": "object", "properties": {"success": {"type": "boolean", "description": "true on success."}, "categories": {"type": "array", "description": "Ordered list of category buckets: \"All\" first, then Projects / Venture Capitals / Influencers, then remaining categories by count descending, \"NoCategory\" last."}}}, "example": {"success": true, "categories": [{"name": "All", "id": null, "cnt": 48213}, {"name": "Projects", "id": 1, "cnt": 15340}, {"name": "Venture Capitals", "id": 3, "cnt": 1872}, {"name": "Influencers", "id": 2, "cnt": 9865}, {"name": "Founders", "id": 5, "cnt": 7421}, {"name": "Exchanges", "id": 7, "cnt": 312}, {"name": "NoCategory", "id": 0, "cnt": 16110}]}}}}, "default": {"$ref": "#/components/responses/Error"}}, "x-error-codes": ["api_key_missing", "api_key_invalid", "api_access_deactivated", "api_key_revoked", "invalid_params", "account_not_found", "method_not_allowed", "rate_limited", "quota_exceeded", "server_error"], "x-one-of-required": [["username", "twitter_id"]]}}, "/api/v1/get_followers": {"get": {"operationId": "get_followers", "summary": "Paginated list of an account's smart followers, filterable by category, tag or tag-category.", "description": "Returns the accounts in TwitterScore's monitored (\"smart\") set that currently follow the target account, one page at a time. Identify the target by `username` or `twitter_id`. Each row is a follower profile with its Twitter Score, follower count, tags, categories and `subscribed_at` (when TwitterScore first recorded the follow). Rows can be narrowed to followers that carry a given category (`category_id`), tag (`tag_id`) or whose tags belong to a given tag-category (`tag_category_id`); several filters combine with AND. Sorting is by Twitter Score (default), by follower count or by `subscribed_at`, descending unless `sort=asc`. Note the data key is `top_followers` (legacy name) and the page size is capped at 25. Only followers that are active, not suspended and on TwitterScore monitoring are counted; the raw X follower list is not exposed. The filter ids are passed straight to the database lookup, so a non-numeric value (e.g. `tag_id=abc`) is not rejected as a parameter error but surfaces as `server_error` (\"Unexpected error [...]\").\n\n**Required:** one of `username`, `twitter_id`.\n**Nested fields**\n\n- `top_followers[].twitter_id` (string): Follower's X user id, as a string (ids exceed 2^53).\n- `top_followers[].username` (string): Follower's X handle.\n- `top_followers[].name` (string): Follower's display name.\n- `top_followers[].description` (string): Follower's X bio (may be empty string).\n- `top_followers[].twitter_score` (number): Follower's Twitter Score on the 0-1000 scale (stored as float).\n- `top_followers[].followers_count` (integer): Follower's own current X follower count.\n- `top_followers[].profile_image` (string): URL of the follower's avatar hosted by TwitterScore (ImageField .url), or null.\n- `top_followers[].tags` (array): Tags attached to the follower.\n- `top_followers[].tags[].id` (integer): Tag id.\n- `top_followers[].tags[].name` (string): Tag name.\n- `top_followers[].categories` (array): Account categories attached to the follower.\n- `top_followers[].categories[].id` (integer): Category id.\n- `top_followers[].categories[].name` (string): Category name.\n- `top_followers[].subscribed_at` (string): ISO 8601 UTC datetime with millisecond precision and Z suffix (e.g. 2023-04-18T09:12:44.318Z; Django JSON encoder) when TwitterScore first recorded this follow. Not the real follow date on X.", "tags": ["Followers"], "security": [{"ApiKeyHeader": []}, {"BearerKey": []}, {"ApiKeyQuery": []}], "parameters": [{"name": "username", "in": "query", "required": false, "description": "X handle of the target account (with or without @, trailing slash and surrounding whitespace tolerated). Resolved against current username, then slug, then previous usernames. Either `username` or `twitter_id` is required.", "schema": {"type": "string"}, "example": "VitalikButerin"}, {"name": "twitter_id", "in": "query", "required": false, "description": "Numeric X user id of the target account. Looked up first when both are given; if no account has this id the lookup falls back to `username`.", "schema": {"type": "integer"}, "example": 295218901}, {"name": "page", "in": "query", "required": false, "description": "1-based page number. Non-numeric, 0 or negative falls back to 1; a page past the last one returns an empty list.", "schema": {"type": "integer", "default": 1}, "example": 1}, {"name": "size", "in": "query", "required": false, "description": "Rows per page. Values above 25 are clamped to 25; 0 or negative becomes 1; non-numeric falls back to 10.", "schema": {"type": "integer", "default": 10, "maximum": 25}, "example": 10}, {"name": "by", "in": "query", "required": false, "description": "Sort column. `score` = follower's Twitter Score, `followers` = follower's own follower count, `subscribed_at` = when TwitterScore first saw the follow. Any other value returns success=false with message \"Sorting is only possible by [followers|score|subscribed_at]\".", "schema": {"type": "string", "enum": ["score", "followers", "subscribed_at"], "default": "score"}, "example": "score"}, {"name": "sort", "in": "query", "required": false, "description": "Sort direction. `asc`/`ascending` (case-insensitive) for ascending; anything else is descending.", "schema": {"type": "string", "enum": ["asc", "desc", "ascending", "descending"], "default": "desc"}, "example": "desc"}, {"name": "category_id", "in": "query", "required": false, "description": "Keep only followers that have this account category (id from get_categories). Non-numeric value → server_error.", "schema": {"type": "integer"}, "example": 3}, {"name": "tag_id", "in": "query", "required": false, "description": "Keep only followers that carry this tag (id from get_tags). Non-numeric value → server_error.", "schema": {"type": "integer"}, "example": 11}, {"name": "tag_category_id", "in": "query", "required": false, "description": "Keep only followers that carry at least one tag belonging to this tag-category. Non-numeric value → server_error.", "schema": {"type": "integer"}, "example": 2}], "responses": {"200": {"description": "Success", "content": {"application/json": {"schema": {"type": "object", "properties": {"success": {"type": "boolean", "description": "true on success."}, "total": {"type": "integer", "description": "Total number of matching follower rows across all pages."}, "page": {"type": "integer", "description": "Page number served (floored at 1)."}, "size": {"type": "integer", "description": "Number of rows actually returned on this page."}, "pages": {"type": "integer", "description": "Total page count, at least 1."}, "top_followers": {"type": "array", "description": "Follower rows for this page."}}}, "example": {"success": true, "total": 4821, "page": 1, "size": 2, "pages": 2411, "top_followers": [{"twitter_id": "295218901", "username": "VitalikButerin", "name": "vitalik.eth", "description": "mi pinxe lo crino tcati", "twitter_score": 1000, "followers_count": 5812345, "profile_image": "https://twitterscore.io/media/profiles/VitalikButerin.jpg", "tags": [{"id": 11, "name": "Ethereum"}], "categories": [{"id": 3, "name": "Influencers"}], "subscribed_at": "2023-04-18T09:12:44.318Z"}, {"twitter_id": "2312333412", "username": "ethereum", "name": "Ethereum", "description": "Ethereum is a global, open-source platform for decentralized applications.", "twitter_score": 912, "followers_count": 3654210, "profile_image": "https://twitterscore.io/media/profiles/ethereum.jpg", "tags": [{"id": 11, "name": "Ethereum"}], "categories": [{"id": 1, "name": "Projects"}], "subscribed_at": "2022-11-02T17:40:05.002Z"}]}}}}, "default": {"$ref": "#/components/responses/Error"}}, "x-error-codes": ["api_key_missing", "api_key_invalid", "api_access_deactivated", "api_key_revoked", "invalid_params", "account_not_found", "method_not_allowed", "rate_limited", "quota_exceeded", "server_error"], "x-one-of-required": [["username", "twitter_id"]]}}, "/api/v1/get_followers_diff": {"get": {"operationId": "get_followers_diff", "summary": "Follower-count change over the last week and last month", "description": "Returns how much the account's follower count changed over the trailing 7 and 30 days, computed from TwitterScore's daily audience snapshots. For each window the response gives the baseline snapshot date (the oldest snapshot inside the window), the signed numeric difference between the newest and that oldest snapshot, and the same difference pre-formatted as a signed string. With a single snapshot in a window the diff is 0 but date is still that snapshot's date; with no snapshot in the window (including profiles that TwitterScore does not monitor) date is null and diff is 0.\n\n**Required:** one of `username`, `twitter_id`.\n**Nested fields**\n\n- `week.date` (string): Date (YYYY-MM-DD) of the oldest snapshot inside the 7-day window, i.e. the baseline the diff is measured from. null when no snapshot exists in the window.\n- `week.diff` (integer): Newest snapshot followers minus baseline snapshot followers; negative when followers were lost; 0 when fewer than two snapshots exist.\n- `week.diff_str` (string): diff as a signed string: \"+3127\", \"-45\" or \"0\".\n- `month.date` (string): Baseline snapshot date for the 30-day window, or null.\n- `month.diff` (integer): Signed follower change over the 30-day window.\n- `month.diff_str` (string): diff as a signed string.", "tags": ["Followers", "History"], "security": [{"ApiKeyHeader": []}, {"BearerKey": []}, {"ApiKeyQuery": []}], "parameters": [{"name": "username", "in": "query", "required": false, "description": "X/Twitter handle of the account, without the leading @. Surrounding whitespace and a trailing slash are tolerated; renamed handles are resolved via previous-username history.", "schema": {"type": "string"}, "example": "VitalikButerin"}, {"name": "twitter_id", "in": "query", "required": false, "description": "Numeric X/Twitter user id of the account. Tried first when both are sent; falls back to username if the id resolves nothing.", "schema": {"type": "integer"}, "example": 295218901}], "responses": {"200": {"description": "Success", "content": {"application/json": {"schema": {"type": "object", "properties": {"success": {"type": "boolean", "description": "true on success."}, "today": {"type": "string", "description": "Current UTC date in YYYY-MM-DD at the time the response was generated (may be up to an hour stale because successful responses are cached)."}, "week": {"type": "object", "description": "Change over the trailing 7 days."}, "month": {"type": "object", "description": "Change over the trailing 30 days, same shape as week."}}}, "example": {"success": true, "today": "2026-10-03", "week": {"date": "2026-09-26", "diff": 3127, "diff_str": "+3127"}, "month": {"date": "2026-09-03", "diff": 14890, "diff_str": "+14890"}}}}}, "default": {"$ref": "#/components/responses/Error"}}, "x-error-codes": ["api_key_missing", "api_key_invalid", "api_access_deactivated", "api_key_revoked", "invalid_params", "account_not_found", "method_not_allowed", "rate_limited", "quota_exceeded", "server_error"], "x-one-of-required": [["username", "twitter_id"]]}}, "/api/v1/get_followers_score_sum": {"get": {"operationId": "get_followers_score_sum", "summary": "Aggregated Twitter Score sum and count of an account's smart followers, optionally filtered by tags/categories.", "description": "Returns the target account's basic profile together with two aggregates over its smart followers (monitored, active, non-suspended accounts that follow it): `smart_followers_count` and `followers_score_sum`, the sum of those followers' Twitter Scores. Pass comma-separated `tag_ids` and/or `category_ids` to aggregate only over followers that carry any of the given tags (OR) and/or any of the given categories (OR); when both are given they combine with AND, and each follower is counted once even if it matches several ids. The `tags` and `categories` in the response describe the TARGET account, not its followers, and use the `tag_id`/`tag_name` and `category_id`/`category_name` key names. Useful for a single-call \"quality of audience\" number without paging through get_followers.\n\n**Required:** one of `username`, `twitter_id`.\n**Nested fields**\n\n- `tags[].tag_id` (integer): Tag id.\n- `tags[].tag_name` (string): Tag name.\n- `categories[].category_id` (integer): Category id.\n- `categories[].category_name` (string): Category name.", "tags": ["Followers"], "security": [{"ApiKeyHeader": []}, {"BearerKey": []}, {"ApiKeyQuery": []}], "parameters": [{"name": "username", "in": "query", "required": false, "description": "X handle of the target account (trailing slash / whitespace tolerated; resolved via current username, slug, then previous usernames). Either `username` or `twitter_id` is required.", "schema": {"type": "string"}, "example": "VitalikButerin"}, {"name": "twitter_id", "in": "query", "required": false, "description": "Numeric X user id of the target account. Looked up first; falls back to `username` if no account has this id.", "schema": {"type": "integer"}, "example": 295218901}, {"name": "category_ids", "in": "query", "required": false, "description": "Comma-separated account-category ids; a follower matches if it has ANY of them. Whitespace around items is trimmed, empty items skipped. If any item is not an integer the whole list is ignored (no error).", "schema": {"type": "string"}, "example": "1,3"}, {"name": "tag_ids", "in": "query", "required": false, "description": "Comma-separated tag ids; a follower matches if it has ANY of them. Combined with `category_ids` using AND. If any item is not an integer the whole list is ignored (no error).", "schema": {"type": "string"}, "example": "11,42"}], "responses": {"200": {"description": "Success", "content": {"application/json": {"schema": {"type": "object", "properties": {"success": {"type": "boolean", "description": "true on success."}, "twitter_id": {"type": "string", "description": "Target account's X user id as a string."}, "username": {"type": "string", "description": "Target account's X handle."}, "name": {"type": ["string", "null"], "description": "Target account's display name."}, "description": {"type": "string", "description": "Target account's X bio."}, "profile_image": {"type": ["string", "null"], "description": "Avatar URL hosted by TwitterScore, or null."}, "twitter_score": {"type": "number", "description": "Target account's Twitter Score (0-1000, float)."}, "followers_count": {"type": "integer", "description": "Target account's current X follower count."}, "smart_followers_count": {"type": "integer", "description": "Number of smart followers matching the filters (distinct accounts when filters are set)."}, "followers_score_sum": {"type": "number", "description": "Sum of the matching smart followers' Twitter Scores; 0 when none."}, "tags": {"type": "array", "description": "Tags of the TARGET account (empty list if the target resolved to a Friend record, which has no tag relations)."}, "categories": {"type": "array", "description": "Categories of the TARGET account (empty list for a Friend record)."}}}, "example": {"success": true, "twitter_id": "295218901", "username": "VitalikButerin", "name": "vitalik.eth", "description": "mi pinxe lo crino tcati", "profile_image": "https://twitterscore.io/media/profiles/VitalikButerin.jpg", "twitter_score": 1000, "followers_count": 5812345, "smart_followers_count": 18342, "followers_score_sum": 1267540.5, "tags": [{"tag_id": 11, "tag_name": "Ethereum"}], "categories": [{"category_id": 3, "category_name": "Influencers"}]}}}}, "default": {"$ref": "#/components/responses/Error"}}, "x-error-codes": ["api_key_missing", "api_key_invalid", "api_access_deactivated", "api_key_revoked", "invalid_params", "account_not_found", "method_not_allowed", "rate_limited", "quota_exceeded", "server_error"], "x-one-of-required": [["username", "twitter_id"]]}}, "/api/v1/get_mentioners": {"get": {"operationId": "get_mentioners", "summary": "Paginated list of unique accounts that mentioned a target account in a period, with the target's profile.", "description": "Returns the distinct tracked accounts that authored at least one tweet mentioning the target within a time window, sorted by their Twitter Score (default) or follower count, up to 100 per page. The window is either a rolling `days` back from the current moment (1, 7 or 30, default 7) or an explicit inclusive calendar range `date_from`..`date_to` (both required together, YYYY-MM-DD, interpreted as UTC midnight bounds); when a range is given `days` is ignored. Only authors present in TwitterScore's account table are listed (they are the only ones with a score to sort by), and the target never counts as its own mentioner. `tag_id` / `category_id` keep only mentioners carrying that tag / category (AND when both given). The response also carries the target's profile (`twitter_id`, `username`, `name`, `description`, `followers_count`, `profile_image`, `tags`, `categories`) and echoes the window as either `days` or `date_from`+`date_to`. Each mentioner row has `twitter_id`, `username`, `name`, `profile_image`, `blue_verified`, `twitter_score`, `followers_count`.\n\n**Required:** one of `username`, `twitter_id`.\n**Nested fields**\n\n- `tags[].tag_id` (integer): Tag id.\n- `tags[].tag_name` (string): Tag name.\n- `categories[].category_id` (integer): Category id.\n- `categories[].category_name` (string): Category name.\n- `mentioners[].twitter_id` (string): Mentioner's X user id as a string.\n- `mentioners[].username` (string): Mentioner's handle.\n- `mentioners[].name` (string): Mentioner's display name.\n- `mentioners[].profile_image` (string): Mentioner's avatar URL, or null.\n- `mentioners[].blue_verified` (boolean): X verified badge.\n- `mentioners[].twitter_score` (number): Mentioner's Twitter Score (0-1000, float).\n- `mentioners[].followers_count` (integer): Mentioner's current follower count.", "tags": ["Mentions"], "security": [{"ApiKeyHeader": []}, {"BearerKey": []}, {"ApiKeyQuery": []}], "parameters": [{"name": "username", "in": "query", "required": false, "description": "X handle of the target account. Either `username` or `twitter_id` is required.", "schema": {"type": "string"}, "example": "VitalikButerin"}, {"name": "twitter_id", "in": "query", "required": false, "description": "Numeric X user id of the target account. Looked up first; falls back to `username` on a miss.", "schema": {"type": "integer"}, "example": 295218901}, {"name": "days", "in": "query", "required": false, "description": "Rolling lookback window: 1, 7 or 30 days back from the request time (not aligned to midnight). Non-numeric falls back to 7; other numbers return success=false with message \"Days params is only available: 1|7|30\". Ignored when `date_from`/`date_to` are given.", "schema": {"type": "integer", "enum": ["1", "7", "30"], "default": 7}, "example": 7}, {"name": "date_from", "in": "query", "required": false, "description": "Start date (inclusive, from 00:00 UTC) in YYYY-MM-DD. Must be sent together with `date_to` (else message \"`date_from` and `date_to` must be provided together\"), parse as YYYY-MM-DD (else \"Invalid date format. Use YYYY-MM-DD\") and be on or before `date_to` (else \"`date_from` must be on or before `date_to`\"). All three are success=false responses.", "schema": {"type": "string"}, "example": "2026-09-01"}, {"name": "date_to", "in": "query", "required": false, "description": "End date (inclusive, whole day up to 24:00 UTC) in YYYY-MM-DD. Must be sent together with `date_from`.", "schema": {"type": "string"}, "example": "2026-09-30"}, {"name": "by", "in": "query", "required": false, "description": "Sort column for mentioners: `score` (Twitter Score) or `followers` (follower count). Any other value returns success=false with message \"Sorting is only possible by [score|followers]\".", "schema": {"type": "string", "enum": ["score", "followers"], "default": "score"}, "example": "score"}, {"name": "sort", "in": "query", "required": false, "description": "`asc`/`ascending` (case-insensitive) for ascending; anything else descending.", "schema": {"type": "string", "enum": ["asc", "desc", "ascending", "descending"], "default": "desc"}, "example": "desc"}, {"name": "tag_id", "in": "query", "required": false, "description": "Keep only mentioners carrying this tag (single integer id; non-numeric is silently ignored).", "schema": {"type": "integer"}, "example": 11}, {"name": "category_id", "in": "query", "required": false, "description": "Keep only mentioners with this account category (single integer id; non-numeric is silently ignored).", "schema": {"type": "integer"}, "example": 3}, {"name": "page", "in": "query", "required": false, "description": "1-based page number. Non-numeric, 0 or negative falls back to 1; a page past the last returns an empty list.", "schema": {"type": "integer", "default": 1}, "example": 1}, {"name": "size", "in": "query", "required": false, "description": "Rows per page. Clamped to 100; 0 or negative becomes 1; non-numeric falls back to 10.", "schema": {"type": "integer", "default": 10, "maximum": 100}, "example": 10}], "responses": {"200": {"description": "Success", "content": {"application/json": {"schema": {"type": "object", "properties": {"success": {"type": "boolean", "description": "true on success."}, "total": {"type": "integer", "description": "Number of distinct mentioners matching the window and filters."}, "page": {"type": "integer", "description": "Page number served."}, "size": {"type": "integer", "description": "Rows returned on this page."}, "pages": {"type": "integer", "description": "Total page count, at least 1."}, "twitter_id": {"type": "string", "description": "Target account's X user id as a string."}, "username": {"type": "string", "description": "Target account's handle."}, "name": {"type": ["string", "null"], "description": "Target account's display name."}, "description": {"type": "string", "description": "Target account's X bio."}, "followers_count": {"type": "integer", "description": "Target account's current follower count."}, "profile_image": {"type": ["string", "null"], "description": "Target account's avatar URL, or null."}, "tags": {"type": "array", "description": "Target account's tags: [{tag_id, tag_name}] (empty list for a Friend-resolved target)."}, "categories": {"type": "array", "description": "Target account's categories: [{category_id, category_name}] (empty list for a Friend-resolved target)."}, "days": {"type": "integer", "description": "Window in days; present only when no date range was given."}, "date_from": {"type": "string", "description": "Echo of the request value; present only when a date range was given."}, "date_to": {"type": "string", "description": "Echo of the request value; present only when a date range was given."}, "mentioners": {"type": "array", "description": "Distinct mentioning accounts on this page."}}}, "example": {"success": true, "total": 1136, "page": 1, "size": 2, "pages": 568, "twitter_id": "295218901", "username": "VitalikButerin", "name": "vitalik.eth", "description": "mi pinxe lo crino tcati", "followers_count": 5812345, "profile_image": "https://twitterscore.io/media/profiles/VitalikButerin.jpg", "tags": [{"tag_id": 11, "tag_name": "Ethereum"}], "categories": [{"category_id": 3, "category_name": "Influencers"}], "days": 7, "mentioners": [{"twitter_id": "2312333412", "username": "ethereum", "name": "Ethereum", "profile_image": "https://twitterscore.io/media/profiles/ethereum.jpg", "blue_verified": true, "twitter_score": 912, "followers_count": 3654210}, {"twitter_id": "574032254", "username": "coinbase", "name": "Coinbase", "profile_image": "https://twitterscore.io/media/profiles/coinbase.jpg", "blue_verified": true, "twitter_score": 871, "followers_count": 7120456}]}}}}, "default": {"$ref": "#/components/responses/Error"}}, "x-error-codes": ["api_key_missing", "api_key_invalid", "api_access_deactivated", "api_key_revoked", "invalid_params", "account_not_found", "method_not_allowed", "rate_limited", "quota_exceeded", "server_error"], "x-one-of-required": [["username", "twitter_id"]]}}, "/api/v1/get_mentions_feed": {"get": {"operationId": "get_mentions_feed", "summary": "Paginated feed of tweets that mention an account, with engagement metrics and enriched author data.", "description": "Returns the tweets stored by TwitterScore that @-mention the target account, newest first by default, one page at a time (max 25 per page). Each item carries the tweet text (t.co short links are replaced with their display form, remaining t.co links stripped), creation time, favorites/retweets/replies/views, attached media and URLs, the quoted tweet when there is one, and an `author` block with the author's Twitter Score, follower count, tags and categories (those four are present only for authors that exist in TwitterScore's account table; for other authors they are null/empty). The target's own tweets that mention itself are excluded. Optional author filters (`tag_id`, `category_id`, `tag_category_id`) keep only mentions written by accounts carrying that tag/category; when any author filter is set, authors unknown to the account table are dropped. `by` selects the sort column (tweet time, an engagement counter, or the author's Twitter Score). The filter ids are passed straight to the database lookup, so a non-numeric value surfaces as `server_error` rather than `invalid_params`.\n\n**Required:** one of `username`, `twitter_id`.\n**Nested fields**\n\n- `mentions[].id` (string): Tweet id as a string.\n- `mentions[].text` (string): Tweet text with t.co links replaced by their display URL and remaining t.co links stripped; null if the stored tweet has no text.\n- `mentions[].created_at` (string): Tweet creation time, ISO 8601 with offset via datetime.isoformat() (e.g. 2026-10-02T14:03:21+00:00). The column is NOT NULL in the database, so it is always present in practice.\n- `mentions[].favorites` (integer): Like count.\n- `mentions[].retweets` (integer): Retweet count.\n- `mentions[].replies` (integer): Reply count.\n- `mentions[].views` (integer): View count.\n- `mentions[].author` (object): Tweet author.\n- `mentions[].author.twitter_id` (string): Author's X user id as a string.\n- `mentions[].author.username` (string): Author handle; \"unknown\" if the author is in neither the account nor the friend table.\n- `mentions[].author.name` (string): Author display name; \"Unknown\" if not known.\n- `mentions[].author.profile_image` (string): Avatar URL: the stored AWS image URL (absolute) when present, otherwise the local image path prefixed with MEDIA_URL (\"/media/...\", relative), or empty string when none.\n- `mentions[].author.blue_verified` (boolean): X verified badge; false when the author is unknown.\n- `mentions[].author.followers_count` (integer): Author's current follower count; null when the author is not in the account table.\n- `mentions[].author.twitter_score` (number): Author's Twitter Score (0-1000, float); null when not in the account table.\n- `mentions[].author.tags` (array): Author's tags as [{id, name}]; empty when unknown.\n- `mentions[].author.categories` (array): Author's categories as [{id, name}]; empty when unknown.\n- `mentions[].media` (array): Attached media: [{url, type}].\n- `mentions[].media[].url` (string): Media URL (media_url column).\n- `mentions[].media[].type` (string): Media type as reported by X (media_type column, e.g. photo, video).\n- `mentions[].urls` (array): Links in the tweet: [{url, expanded_url, display_url}].\n- `mentions[].urls[].url` (string): Original URL (replaced by display_url when it was a t.co link found in the text).\n- `mentions[].urls[].expanded_url` (string): Fully expanded URL.\n- `mentions[].urls[].display_url` (string): Short display form.\n- `mentions[].quoted_tweet` (object): Present only when the tweet quotes another tweet that is also stored; same shape as a mention item (including the extended author block) minus `quoted_tweet`. Absent (not null) otherwise.", "tags": ["Mentions"], "security": [{"ApiKeyHeader": []}, {"BearerKey": []}, {"ApiKeyQuery": []}], "parameters": [{"name": "username", "in": "query", "required": false, "description": "X handle of the mentioned (target) account. Either `username` or `twitter_id` is required.", "schema": {"type": "string"}, "example": "VitalikButerin"}, {"name": "twitter_id", "in": "query", "required": false, "description": "Numeric X user id of the target account. Looked up first; falls back to `username` on a miss.", "schema": {"type": "integer"}, "example": 295218901}, {"name": "page", "in": "query", "required": false, "description": "1-based page number. Non-numeric, 0 or negative falls back to 1; a page past the last returns an empty list.", "schema": {"type": "integer", "default": 1}, "example": 1}, {"name": "size", "in": "query", "required": false, "description": "Tweets per page. Clamped to 25; 0 or negative becomes 1; non-numeric falls back to 10.", "schema": {"type": "integer", "default": 10, "maximum": 25}, "example": 10}, {"name": "by", "in": "query", "required": false, "description": "Sort column: `created_at` (tweet time), `favorites`, `retweets`, `replies`, `views`, or `score` (author's Twitter Score; authors absent from the account table sort as NULL — first in ascending, last in descending order on MySQL/MariaDB). Any other value returns success=false with message \"Sorting is only possible by [created_at|favorites|retweets|replies|views|score]\".", "schema": {"type": "string", "enum": ["created_at", "favorites", "retweets", "replies", "views", "score"], "default": "created_at"}, "example": "created_at"}, {"name": "sort", "in": "query", "required": false, "description": "`asc`/`ascending` (case-insensitive) for ascending; anything else descending.", "schema": {"type": "string", "enum": ["asc", "desc", "ascending", "descending"], "default": "desc"}, "example": "desc"}, {"name": "tag_id", "in": "query", "required": false, "description": "Keep only mentions whose author carries this tag. Non-numeric value → server_error.", "schema": {"type": "integer"}, "example": 11}, {"name": "category_id", "in": "query", "required": false, "description": "Keep only mentions whose author has this account category. Non-numeric value → server_error.", "schema": {"type": "integer"}, "example": 3}, {"name": "tag_category_id", "in": "query", "required": false, "description": "Keep only mentions whose author has at least one tag in this tag-category. Non-numeric value → server_error.", "schema": {"type": "integer"}, "example": 2}], "responses": {"200": {"description": "Success", "content": {"application/json": {"schema": {"type": "object", "properties": {"success": {"type": "boolean", "description": "true on success."}, "total": {"type": "integer", "description": "Total number of matching mentions."}, "page": {"type": "integer", "description": "Page number served."}, "size": {"type": "integer", "description": "Number of tweets returned on this page."}, "pages": {"type": "integer", "description": "Total page count, at least 1."}, "mentions": {"type": "array", "description": "Tweets on this page."}}}, "example": {"success": true, "total": 3156, "page": 1, "size": 1, "pages": 3156, "mentions": [{"id": "1841234567890123456", "text": "Great thread by @VitalikButerin on account abstraction vitalik.eth.limo", "created_at": "2026-10-02T14:03:21+00:00", "favorites": 412, "retweets": 57, "replies": 23, "views": 88214, "author": {"twitter_id": "1456789012", "username": "cryptodev_eth", "name": "Crypto Dev", "profile_image": "https://twitterscore.io/media/profiles/cryptodev_eth.jpg", "blue_verified": true, "followers_count": 48213, "twitter_score": 312, "tags": [{"id": 11, "name": "Ethereum"}], "categories": [{"id": 3, "name": "Influencers"}]}, "media": [{"url": "https://pbs.twimg.com/media/GZabc123.jpg", "type": "photo"}], "urls": [{"url": "vitalik.eth.limo", "expanded_url": "https://vitalik.eth.limo/", "display_url": "vitalik.eth.limo"}]}]}}}}, "default": {"$ref": "#/components/responses/Error"}}, "x-error-codes": ["api_key_missing", "api_key_invalid", "api_access_deactivated", "api_key_revoked", "invalid_params", "account_not_found", "method_not_allowed", "rate_limited", "quota_exceeded", "server_error"], "x-one-of-required": [["username", "twitter_id"]]}}, "/api/v1/get_smart_follows": {"get": {"operationId": "get_smart_follows", "summary": "Accounts ranked by smart-follower momentum (Smart Follows page) over 1, 7 or 30 days", "description": "Returns the ranked list behind the Smart Follows page (formerly \"Alpha\"): non-suspended accounts that are followed by at least one \"smart\" account in the chosen bucket, with how many smart followers they have in total, how many of those (re)followed them during the window, and — as the default sort key — the change in that new-follower count versus the previous window. Buckets select which smart-follower set is counted: `all`, `vc` (venture capitals), `inf` (influencers) or `angels`. Default ranking is `by=alpha` descending: the accounts gaining the most new smart followers compared with the previous period come first; `sort=asc` flips it to show accounts losing smart-follower momentum. `by=score` and `by=followers` rank by the account's current Twitter Score and follower count instead. Ties are broken by current Twitter Score (descending) and duplicate usernames are collapsed. Rows are read from a daily pre-computed, ranked slice of at most 1000 rows, so `total` never exceeds 1000. Each row also carries a short list of the account's most notable smart followers (`top_alpha_followers`) for display as an avatar stack. The response echoes the applied `days` and `bucket`.\n**Nested fields**\n\n- `data[].twitter_id` (string): Account's numeric Twitter/X id as a string (empty string if missing).\n- `data[].username` (string): Handle without the @.\n- `data[].name` (string): Display name; falls back to the username when empty.\n- `data[].profile_url` (string): Relative TwitterScore profile path, \"/twitter/{slug}\" (slug falls back to username; \"#\" when both are empty). Prefix with https://twitterscore.io.\n- `data[].profile_image` (string): Avatar URL: the TwitterScore S3 copy (aws_image_url), else the original Twitter image URL (tw_image_url), else null.\n- `data[].blue_verified` (boolean): Whether the account has X Premium (blue) verification.\n- `data[].description` (string): Profile bio (empty string when none).\n- `data[].twitter_score` (integer): Current Twitter Score (0-1000), integer.\n- `data[].twitter_score_diff` (integer): Twitter Score change over the window (period snapshot score_diff); 0 when the account has no period snapshot.\n- `data[].current_followers` (integer): Current follower count.\n- `data[].followers_diff` (integer): current_followers minus followers at the start of the window; may be negative.\n- `data[].total_alpha_followers` (integer): Number of currently active smart followers in the selected bucket (always > 0 — the view excludes rows with zero).\n- `data[].new_alpha_followers_diff` (integer): Number of smart followers from the bucket that (re)followed the account during the window (new_alpha_followers_count). Note: this is the period count, not the versus-previous-period delta (new_diff) that `by=alpha` sorts on; new_diff itself is not exposed.\n- `data[].top_alpha_followers` (array): Short list of notable smart followers for display (avatar stack); empty array when none. Entries without a name/username are dropped.\n- `data[].top_alpha_followers[].name` (string): Follower's display name (falls back to username).\n- `data[].top_alpha_followers[].username` (string): Follower's handle (may be empty string).\n- `data[].top_alpha_followers[].image` (string): Follower's avatar URL (empty string when none).\n- `data[].top_alpha_followers[].score` (integer): Follower's Twitter Score, rounded to an integer.\n- `data[].top_alpha_followers[].verified` (boolean): Follower's blue-verified flag.", "tags": ["Lists"], "security": [{"ApiKeyHeader": []}, {"BearerKey": []}, {"ApiKeyQuery": []}], "parameters": [{"name": "days", "in": "query", "required": false, "description": "Period window in days: 1, 7 or 30. Any other integer returns {\"success\": false, \"message\": \"Days param is only available: 1|7|30\"}; a non-numeric value falls back to 7.", "schema": {"type": "integer", "enum": ["1", "7", "30"], "default": 7}, "example": 7}, {"name": "by", "in": "query", "required": false, "description": "Ranking column: `alpha` = change in the number of new smart followers versus the previous period (internal column new_diff), `score` = current Twitter Score (current_project_score), `followers` = current follower count (current_followers). Case-sensitive; any other value returns {\"success\": false, \"message\": \"Sorting is only possible by [score|followers|alpha]\"}.", "schema": {"type": "string", "enum": ["alpha", "score", "followers"], "default": "alpha"}, "example": "alpha"}, {"name": "sort", "in": "query", "required": false, "description": "Sort direction: `asc` or `ascending` (case-insensitive) for ascending, anything else (including the default) for descending.", "schema": {"type": "string", "enum": ["desc", "asc", "descending", "ascending"], "default": "desc"}, "example": "desc"}, {"name": "bucket", "in": "query", "required": false, "description": "Which smart-follower set is counted: `all`, `vc` (venture capitals), `inf` (influencers) or `angels`. Case-insensitive, whitespace trimmed; unknown values silently fall back to `all` (no error).", "schema": {"type": "string", "enum": ["all", "vc", "inf", "angels"], "default": "all"}, "example": "vc"}, {"name": "page", "in": "query", "required": false, "description": "1-based page number. Non-numeric, 0 or negative values are treated as 1; a page past the last returns an empty `data` array.", "schema": {"type": "integer", "default": 1}, "example": 1}, {"name": "size", "in": "query", "required": false, "description": "Rows per page, capped at 100. Values below 1 are treated as 1; a non-numeric value falls back to 10.", "schema": {"type": "integer", "default": 10, "maximum": 100}, "example": 10}], "responses": {"200": {"description": "Success", "content": {"application/json": {"schema": {"type": "object", "properties": {"success": {"type": "boolean", "description": "true for a served page."}, "total": {"type": "integer", "description": "Ranked rows available for this days/bucket/sort combination; at most 1000. 0 when the daily cache has not been primed yet."}, "page": {"type": "integer", "description": "Served page number (after clamping to 1)."}, "size": {"type": "integer", "description": "Number of items returned in `data`."}, "pages": {"type": "integer", "description": "Total pages at the requested size; at least 1."}, "days": {"type": "integer", "description": "Applied window (1, 7 or 30)."}, "bucket": {"type": "string", "description": "Applied (resolved) bucket: all, vc, inf or angels."}}}, "example": {"success": true, "total": 1000, "page": 1, "size": 1, "pages": 1000, "days": 7, "bucket": "all", "data": [{"twitter_id": "295218901", "username": "VitalikButerin", "name": "vitalik.eth", "profile_url": "/twitter/VitalikButerin", "profile_image": "https://twitterscore.s3.amazonaws.com/profiles/VitalikButerin.jpg", "blue_verified": true, "description": "mi pinxe lo crino tcati", "twitter_score": 1000, "twitter_score_diff": 0, "current_followers": 5812345, "followers_diff": 14135, "total_alpha_followers": 2417, "new_alpha_followers_diff": 36, "top_alpha_followers": [{"name": "Balaji", "username": "balajis", "image": "https://twitterscore.s3.amazonaws.com/profiles/balajis.jpg", "score": 942, "verified": true}, {"name": "Hasu", "username": "hasufl", "image": "https://twitterscore.s3.amazonaws.com/profiles/hasufl.jpg", "score": 611, "verified": false}]}]}}}}, "default": {"$ref": "#/components/responses/Error"}}, "x-error-codes": ["api_key_missing", "api_key_invalid", "api_access_deactivated", "api_key_revoked", "method_not_allowed", "rate_limited", "quota_exceeded", "server_error"]}}, "/api/v1/get_smart_mentions": {"get": {"operationId": "get_smart_mentions", "summary": "Leaderboard of accounts most mentioned by smart accounts over the last 1, 7 or 30 days (Smart Mentions page).", "description": "Returns the Smart Mentions ranking: up to 1,000 accounts ordered by how many times they were mentioned by TwitterScore-tracked authors in the selected window (`days` = 1, 7 or 30), paginated up to 100 per page. No target account is needed. Each row gives the account's profile, its Twitter Score and follower count with their change over the period (`*_diff_int` plus a human-readable `*_diff_str` with space-grouped thousands), all-time mention totals (`mentions_total`, `unique_mentioners_total`), the period's mentions and unique mentioners (`mentions_diff_int`, `unique_mentioners_diff_int`), the single highest-scored mentioner (`top_mentioner`) and up to six top mentioners (`mentioners_list`), plus the account's categories. `category_id` filters the board to one account category (0 = all, -1 = accounts with no category). `by` changes the ranking column: `mentions` (period mentions, default), `mentioners` (ALL-TIME unique mentioners), `score`, `followers`; ties are broken by all-time mentions descending. Only non-suspended accounts with at least one period mention and at least 2/3/4 distinct mentioning authors (for 1/7/30 days) are listed. Data comes from a pre-aggregated cache refreshed by cron; the extra `days` and `category_name` fields echo the applied filters.\n**Nested fields**\n\n- `data[].twitter_id` (string): X user id as a string.\n- `data[].username` (string): X handle.\n- `data[].name` (string): Display name (falls back to username).\n- `data[].profile_url` (string): Relative TwitterScore profile path like /twitter/VitalikButerin (slug, else username), or \"#\" when neither is set.\n- `data[].profile_image` (string): Avatar URL (AWS copy, else X image URL), or null.\n- `data[].blue_verified` (boolean): X verified badge.\n- `data[].description` (string): X bio (may be empty).\n- `data[].twitter_score` (integer): Current Twitter Score (0-1000) from the period snapshot, truncated to int.\n- `data[].twitter_score_diff_int` (integer): Twitter Score change over the window.\n- `data[].twitter_score_diff_str` (string): Same value formatted with spaces as thousands separators (e.g. \"1 532\", \"-1 532\").\n- `data[].current_followers` (integer): Current follower count.\n- `data[].followers_diff_int` (integer): Follower change over the window.\n- `data[].followers_diff_str` (string): Formatted follower change.\n- `data[].mentions_total` (integer): All-time mentions by tracked authors.\n- `data[].mentions_diff_int` (integer): Mentions received in the window (ranking value for by=mentions; per-author contribution is capped).\n- `data[].mentions_diff_str` (string): Formatted window mentions.\n- `data[].unique_mentioners_total` (integer): All-time distinct mentioning authors (ranking value for by=mentioners).\n- `data[].unique_mentioners_diff_int` (integer): Distinct mentioning authors in the window.\n- `data[].unique_mentioners_diff_str` (string): Formatted window unique mentioners.\n- `data[].top_mentioner` (object): Highest-scored mentioner in the window: {name, username, image, score, verified}; null when none computed.\n- `data[].top_mentioner.name` (string): Mentioner display name (falls back to username).\n- `data[].top_mentioner.username` (string): Mentioner handle.\n- `data[].top_mentioner.image` (string): Mentioner avatar URL or empty string.\n- `data[].top_mentioner.score` (integer): Mentioner Twitter Score, rounded to int.\n- `data[].top_mentioner.verified` (boolean): Mentioner verified badge.\n- `data[].mentioners_list` (array): Up to 6 top mentioners in the window, same object shape as top_mentioner, ordered by Twitter Score desc then mention count desc. Empty for rows outside the top-2000 targets by period mentions.\n- `data[].categories` (array): Account categories: [{id, name}].\n- `data[].categories[].id` (integer): Category id.\n- `data[].categories[].name` (string): Category name.", "tags": ["Mentions", "Lists"], "security": [{"ApiKeyHeader": []}, {"BearerKey": []}, {"ApiKeyQuery": []}], "parameters": [{"name": "days", "in": "query", "required": false, "description": "Lookback window in days: 1, 7 or 30. Non-numeric falls back to 7; any other number returns success=false with message \"Days params is only available: 1|7|30\".", "schema": {"type": "integer", "enum": ["1", "7", "30"], "default": 7}, "example": 7}, {"name": "category_id", "in": "query", "required": false, "description": "Account category filter: 0 = all accounts, -1 = accounts without a category, otherwise a category id from get_categories. Unknown id returns success=false with message \"Category with id=N not found\". Non-numeric falls back to 0.", "schema": {"type": "integer", "default": 0}, "example": 0}, {"name": "by", "in": "query", "required": false, "description": "Ranking column: `mentions` = mentions received in the window, `mentioners` = all-time unique mentioners, `score` = Twitter Score, `followers` = follower count. Any other value returns success=false with message \"Sorting is only possible by [score|followers|mentions|mentioners]\".", "schema": {"type": "string", "enum": ["score", "followers", "mentions", "mentioners"], "default": "mentions"}, "example": "mentions"}, {"name": "sort", "in": "query", "required": false, "description": "`asc`/`ascending` (case-insensitive) for ascending; anything else descending.", "schema": {"type": "string", "enum": ["asc", "desc", "ascending", "descending"], "default": "desc"}, "example": "desc"}, {"name": "page", "in": "query", "required": false, "description": "1-based page number. Non-numeric, 0 or negative falls back to 1; a page past the last returns an empty list.", "schema": {"type": "integer", "default": 1}, "example": 1}, {"name": "size", "in": "query", "required": false, "description": "Rows per page. Clamped to 100; 0 or negative becomes 1; non-numeric falls back to 10.", "schema": {"type": "integer", "default": 10, "maximum": 100}, "example": 10}], "responses": {"200": {"description": "Success", "content": {"application/json": {"schema": {"type": "object", "properties": {"success": {"type": "boolean", "description": "true on success."}, "total": {"type": "integer", "description": "Rows in the ranking after the category filter, at most 1000."}, "page": {"type": "integer", "description": "Page number served."}, "size": {"type": "integer", "description": "Rows returned on this page."}, "pages": {"type": "integer", "description": "Total page count, at least 1."}, "days": {"type": "integer", "description": "Window applied (1, 7 or 30)."}, "category_name": {"type": "string", "description": "\"all\", \"NoCategory\", or the name of the selected category."}, "data": {"type": "array", "description": "Ranked rows (note the generic key name `data`)."}}}, "example": {"success": true, "total": 1000, "page": 1, "size": 1, "pages": 1000, "days": 7, "category_name": "all", "data": [{"twitter_id": "295218901", "username": "VitalikButerin", "name": "vitalik.eth", "profile_url": "/twitter/VitalikButerin", "profile_image": "https://twitterscore.io/media/profiles/VitalikButerin.jpg", "blue_verified": true, "description": "mi pinxe lo crino tcati", "twitter_score": 1000, "twitter_score_diff_int": 0, "twitter_score_diff_str": "0", "current_followers": 5812345, "followers_diff_int": 1532, "followers_diff_str": "1 532", "mentions_total": 184233, "mentions_diff_int": 2417, "mentions_diff_str": "2 417", "unique_mentioners_total": 41208, "unique_mentioners_diff_int": 1136, "unique_mentioners_diff_str": "1 136", "top_mentioner": {"name": "Ethereum", "username": "ethereum", "image": "https://twitterscore.io/media/profiles/ethereum.jpg", "score": 912, "verified": true}, "mentioners_list": [{"name": "Ethereum", "username": "ethereum", "image": "https://twitterscore.io/media/profiles/ethereum.jpg", "score": 912, "verified": true}, {"name": "Coinbase", "username": "coinbase", "image": "https://twitterscore.io/media/profiles/coinbase.jpg", "score": 871, "verified": true}], "categories": [{"id": 3, "name": "Influencers"}]}]}}}}, "default": {"$ref": "#/components/responses/Error"}}, "x-error-codes": ["api_key_missing", "api_key_invalid", "api_access_deactivated", "api_key_revoked", "method_not_allowed", "rate_limited", "quota_exceeded", "server_error"]}}, "/api/v1/get_tagged_followers_count": {"get": {"operationId": "get_tagged_followers_count", "summary": "Number of monitored followers per tag, grouped by tag category", "description": "Counts how many of the account's monitored followers carry each TwitterScore tag (for example Tier 1 VC, Ethereum, CEX), grouped by the tag's category (VC tier, Ecosystems, ...). Groups are ordered by tag-category id ascending and tags inside a group by count descending. A follower with several tags is counted once per tag. Only active monitored followers with an active follow relationship are counted. Tags that are not assigned to any tag category are not included in the output. The data array is empty when none of the account's monitored followers is tagged.\n\n**Required:** one of `username`, `twitter_id`.\n**Nested fields**\n\n- `data[].tag_category_id` (number): Tag-category id. Normally an integer; serialised as a float (e.g. 2.0) whenever at least one follower tag has no tag category, because pandas widens the column to float before grouping.\n- `data[].tag_category_name` (string): Tag-category name (e.g. \"VC Tier\", \"Ecosystems\").\n- `data[].tags` (array): Tags in this category, ordered by cnt descending.\n- `data[].tags[].tag_id` (integer): Tag id as used by get_tags and get_followers filters.\n- `data[].tags[].tag_name` (string): Tag name.\n- `data[].tags[].cnt` (integer): Number of monitored followers carrying this tag.", "tags": ["Followers"], "security": [{"ApiKeyHeader": []}, {"BearerKey": []}, {"ApiKeyQuery": []}], "parameters": [{"name": "username", "in": "query", "required": false, "description": "X/Twitter handle of the account, without the leading @. Surrounding whitespace and a trailing slash are tolerated; renamed handles are resolved via previous-username history.", "schema": {"type": "string"}, "example": "VitalikButerin"}, {"name": "twitter_id", "in": "query", "required": false, "description": "Numeric X/Twitter user id of the account. Tried first when both are sent; falls back to username if the id resolves nothing.", "schema": {"type": "integer"}, "example": 295218901}], "responses": {"200": {"description": "Success", "content": {"application/json": {"schema": {"type": "object", "properties": {"success": {"type": "boolean", "description": "true on success."}, "data": {"type": "array", "description": "One entry per tag category, ordered by tag_category_id ascending. Empty when no monitored follower is tagged."}}}, "example": {"success": true, "data": [{"tag_category_id": 1, "tag_category_name": "VC Tier", "tags": [{"tag_id": 13, "tag_name": "Tier 2 VC", "cnt": 388}, {"tag_id": 12, "tag_name": "Tier 1 VC", "cnt": 143}]}, {"tag_category_id": 2, "tag_category_name": "Ecosystems", "tags": [{"tag_id": 41, "tag_name": "Ethereum", "cnt": 6120}, {"tag_id": 44, "tag_name": "Solana", "cnt": 2310}, {"tag_id": 47, "tag_name": "Base", "cnt": 1175}]}]}}}}, "default": {"$ref": "#/components/responses/Error"}}, "x-error-codes": ["api_key_missing", "api_key_invalid", "api_access_deactivated", "api_key_revoked", "invalid_params", "account_not_found", "method_not_allowed", "rate_limited", "quota_exceeded", "server_error"], "x-one-of-required": [["username", "twitter_id"]]}}, "/api/v1/get_tags": {"get": {"operationId": "get_tags", "summary": "List of account tags with their tag categories", "description": "Returns TwitterScore's fine-grained account tags (e.g. a16z, Coinbase Ventures, Paradigm, Binance, Solana, Ethereum, NFT) together with the tag categories each tag belongs to (e.g. \"Tier 1 VC\"; a tag can sit in several tag categories). Tag ids are the `tag_id` values returned by get_twitter_info. Tag categories are a separate taxonomy from the account categories returned by get_categories. Only tags linked to at least one tag category are listed. Results are ordered by tag id and paginated with `page`/`size` (default 10, max 25).\n**Nested fields**\n\n- `tags[].id` (integer): Tag id (matches `tag_id` in get_twitter_info).\n- `tags[].name` (string): Tag name.\n- `tags[].tag_categories` (array): Tag categories this tag belongs to (one or more).\n- `tags[].tag_categories[].id` (integer): Tag category id.\n- `tags[].tag_categories[].name` (string): Tag category name.", "tags": ["Reference"], "security": [{"ApiKeyHeader": []}, {"BearerKey": []}, {"ApiKeyQuery": []}], "parameters": [{"name": "page", "in": "query", "required": false, "description": "1-based page number. Non-integer or 0 falls back to 1, negatives are floored to 1; a page past the end returns an empty list with the same total/pages.", "schema": {"type": "integer", "default": 1}}, {"name": "size", "in": "query", "required": false, "description": "Items per page; values above 25 are capped to 25, non-integer values fall back to 10, values below 1 are floored to 1.", "schema": {"type": "integer", "default": 10, "maximum": 25}}], "responses": {"200": {"description": "Success", "content": {"application/json": {"schema": {"type": "object", "properties": {"success": {"type": "boolean", "description": "Always true (an internal SQL failure yields an empty list, not an error)."}, "total": {"type": "integer", "description": "Total number of tags that have at least one tag category."}, "page": {"type": "integer", "description": "Page returned (≥1)."}, "size": {"type": "integer", "description": "Number of items in `tags` on this page."}, "pages": {"type": "integer", "description": "Total number of pages (at least 1)."}, "tags": {"type": "array", "description": "Tags ordered by id."}}}, "example": {"success": true, "total": 56, "page": 1, "size": 3, "pages": 19, "tags": [{"id": 1, "name": "a16z", "tag_categories": [{"id": 1, "name": "Tier 1 VC"}]}, {"id": 2, "name": "Coinbase Ventures", "tag_categories": [{"id": 1, "name": "Tier 1 VC"}]}, {"id": 3, "name": "Paradigm", "tag_categories": [{"id": 1, "name": "Tier 1 VC"}]}]}}}}, "default": {"$ref": "#/components/responses/Error"}}, "x-error-codes": ["api_key_missing", "api_key_invalid", "api_access_deactivated", "api_key_revoked", "method_not_allowed", "rate_limited", "quota_exceeded", "server_error"]}}, "/api/v1/get_tokenless": {"get": {"operationId": "get_tokenless", "summary": "Crypto projects without a token, ranked by Twitter Score or follower growth", "description": "Returns the list behind the Airdrop / tokenless page: accounts in the \"Projects\" category that have no linked coin on TwitterScore (no token tracked) and are not on the tokenless blacklist. Each item carries the project's Twitter Score, its current and previous follower counts (as integers and as compact strings like \"318K\") and the difference between them. Rank by Twitter Score (`by=score`, default, uses the absolute current score — not a change) or by follower change (`by=followers`, current minus previous followers). The ranking is capped at the first 1000 rows, so `total` never exceeds 1000. Unlike get_trending, there is no period parameter: the \"previous\" follower count is the previous stored snapshot of the account, and the response has no extra period fields.\n**Nested fields**\n\n- `data[].twitter_id` (string): Project account's numeric Twitter/X id as a string.\n- `data[].name` (string): Display name. Serialised with str(), so an account without a name yields the literal string \"None\".\n- `data[].username` (string): Handle without the @.\n- `data[].description` (string): Profile bio (empty string when none).\n- `data[].twitter_score` (number): Current Twitter Score (0-1000 scale) as a float with decimals, e.g. 851.53 (Account.project_score FloatField).\n- `data[].curr_followers_int` (integer): Current follower count.\n- `data[].curr_followers_str` (string): Current followers as a compact string with K/M/B suffix (3 significant digits), e.g. \"318K\"; \"0\" when zero.\n- `data[].prev_followers_int` (integer): Follower count at the previous snapshot.\n- `data[].prev_followers_str` (string): Previous followers as a compact K/M/B string.\n- `data[].followers_diff_int` (integer): curr_followers_int minus prev_followers_int; may be negative. Sort key for by=followers.\n- `data[].followers_diff_str` (string): followers_diff_int as a compact K/M/B string, e.g. \"12.2K\" or \"-1.5K\"; \"0\" when zero.\n- `data[].img` (string): Avatar URL from the account's stored profile image on S3 (https://<bucket>.s3.amazonaws.com/profiles/<file>). When no image is stored the code builds \"https://twitterscore.io\" + settings.MEDIA_URL + \"profiles/NoImageFound.png\" — \"https://twitterscore.io/media/profiles/NoImageFound.png\" with local media, but a malformed double-scheme URL when USE_S3 is on (see notes).", "tags": ["Lists"], "security": [{"ApiKeyHeader": []}, {"BearerKey": []}, {"ApiKeyQuery": []}], "parameters": [{"name": "by", "in": "query", "required": false, "description": "Ranking column: `score` orders by the current Twitter Score (project_score), `followers` by the follower change (current minus previous followers). Case-sensitive; any other value returns {\"success\": false, \"message\": \"Sorting is only possible by [followers|score]\"}.", "schema": {"type": "string", "enum": ["score", "followers"], "default": "score"}, "example": "followers"}, {"name": "sort", "in": "query", "required": false, "description": "Sort direction: `asc` or `ascending` (case-insensitive) for ascending, anything else (including the default) for descending.", "schema": {"type": "string", "enum": ["desc", "asc", "descending", "ascending"], "default": "desc"}, "example": "desc"}, {"name": "page", "in": "query", "required": false, "description": "1-based page number. Non-numeric, 0 or negative values are treated as 1; a page past the last returns an empty `data` array.", "schema": {"type": "integer", "default": 1}, "example": 1}, {"name": "size", "in": "query", "required": false, "description": "Rows per page, capped at 100. Values below 1 are treated as 1; a non-numeric value falls back to 10.", "schema": {"type": "integer", "default": 10, "maximum": 100}, "example": 10}], "responses": {"200": {"description": "Success", "content": {"application/json": {"schema": {"type": "object", "properties": {"success": {"type": "boolean", "description": "true for a served page."}, "total": {"type": "integer", "description": "Number of matching projects, capped at 1000."}, "page": {"type": "integer", "description": "Served page number."}, "size": {"type": "integer", "description": "Number of items returned in `data`."}, "pages": {"type": "integer", "description": "Total pages at the requested size; at least 1."}}}, "example": {"success": true, "total": 1000, "page": 1, "size": 2, "pages": 500, "data": [{"twitter_id": "1457382190432124930", "name": "MegaETH", "username": "megaeth_labs", "description": "Real-time blockchain. Mainnet soon.", "twitter_score": 412.37, "curr_followers_int": 318204, "curr_followers_str": "318K", "prev_followers_int": 305990, "prev_followers_str": "306K", "followers_diff_int": 12214, "followers_diff_str": "12.2K", "img": "https://twitterscore.s3.amazonaws.com/profiles/megaeth_labs.jpg"}, {"twitter_id": "1623390414587465728", "name": "Monad", "username": "monad_xyz", "description": "Performance-optimized EVM L1.", "twitter_score": 387.05, "curr_followers_int": 742110, "curr_followers_str": "742K", "prev_followers_int": 739870, "prev_followers_str": "740K", "followers_diff_int": 2240, "followers_diff_str": "2.24K", "img": "https://twitterscore.s3.amazonaws.com/profiles/monad_xyz.jpg"}]}}}}, "default": {"$ref": "#/components/responses/Error"}}, "x-error-codes": ["api_key_missing", "api_key_invalid", "api_access_deactivated", "api_key_revoked", "method_not_allowed", "rate_limited", "quota_exceeded", "server_error"]}}, "/api/v1/get_top_researched": {"get": {"operationId": "get_top_researched", "summary": "Top 10 accounts by search or profile-open activity on TwitterScore over the last 24 hours.", "description": "Returns the ten accounts most looked up on twitterscore.io in the past 24 hours — the same data as the /topResearched/ page. `type=searching` (default) ranks by how often the account was searched; `type=opening` ranks by how often its profile page was opened. No target account is needed. Each row is enriched from the account table when an ACTIVE account with that username exists (verified badge, Twitter Score, follower count and its change since the previous snapshot, tags, categories, relative `profile_url`); otherwise `verified`=false, `current_followers`/`followers_diff`=0, `tags`/`categories`=[] and `profile_url`/`twitter_score` come from the lookup log itself. `description` always comes from the log row. `total` is always at most 10, so with the default `size` everything fits on one page. The underlying ranking is cached for 60 minutes.\n**Nested fields**\n\n- `accounts[].username` (string): X handle as recorded in the lookup log.\n- `accounts[].name` (string): Display name from the account table when found, else from the log (falls back to username).\n- `accounts[].profile_url` (string): Relative TwitterScore profile path like /twitter/VitalikButerin when the account is in the table and has a slug; otherwise the absolute URL recorded in the lookup log.\n- `accounts[].profile_image` (string): Avatar URL from the account table when it has an image file, else the image URL recorded in the log, or null.\n- `accounts[].verified` (boolean): X verified badge; false when the account is not in the table.\n- `accounts[].twitter_score` (number): Twitter Score (0-1000, float). From the account table when found, otherwise the project_score recorded in the log row (NOT NULL, default 0).\n- `accounts[].current_followers` (integer): Current follower count; 0 when the account is not in the table.\n- `accounts[].followers_diff` (integer): current_followers minus the previous stored follower count; 0 when unknown.\n- `accounts[].description` (string): X bio as recorded in the log row (may be empty).\n- `accounts[].tags` (array): Tags: [{id, name}]. A tag that belongs to several tag-categories may be listed once per tag-category.\n- `accounts[].tags[].id` (integer): Tag id.\n- `accounts[].tags[].name` (string): Tag name.\n- `accounts[].categories` (array): Account categories: [{id, name}].\n- `accounts[].categories[].id` (integer): Category id.\n- `accounts[].categories[].name` (string): Category name.", "tags": ["Lists"], "security": [{"ApiKeyHeader": []}, {"BearerKey": []}, {"ApiKeyQuery": []}], "parameters": [{"name": "type", "in": "query", "required": false, "description": "Ranking source: `searching` (search queries) or `opening` (profile opens). Whitespace is trimmed; empty falls back to `searching`. Any other value returns success=false with message \"Param `type` must be one of [searching|opening]\".", "schema": {"type": "string", "enum": ["searching", "opening"], "default": "searching"}, "example": "searching"}, {"name": "page", "in": "query", "required": false, "description": "1-based page number. Non-numeric, 0 or negative falls back to 1; a page past the last returns an empty list.", "schema": {"type": "integer", "default": 1}, "example": 1}, {"name": "size", "in": "query", "required": false, "description": "Rows per page. Clamped to 25; 0 or negative becomes 1; non-numeric falls back to 10.", "schema": {"type": "integer", "default": 10, "maximum": 25}, "example": 10}], "responses": {"200": {"description": "Success", "content": {"application/json": {"schema": {"type": "object", "properties": {"success": {"type": "boolean", "description": "true on success."}, "total": {"type": "integer", "description": "Number of ranked accounts, at most 10."}, "page": {"type": "integer", "description": "Page number served."}, "size": {"type": "integer", "description": "Rows returned on this page."}, "pages": {"type": "integer", "description": "Total page count, at least 1."}, "accounts": {"type": "array", "description": "Ranked accounts, most researched first."}}}, "example": {"success": true, "total": 10, "page": 1, "size": 2, "pages": 5, "accounts": [{"username": "VitalikButerin", "name": "vitalik.eth", "profile_url": "/twitter/VitalikButerin", "profile_image": "https://twitterscore.io/media/profiles/VitalikButerin.jpg", "verified": true, "twitter_score": 1000, "current_followers": 5812345, "followers_diff": 1532, "description": "mi pinxe lo crino tcati", "tags": [{"id": 11, "name": "Ethereum"}], "categories": [{"id": 3, "name": "Influencers"}]}, {"username": "ethereum", "name": "Ethereum", "profile_url": "/twitter/ethereum", "profile_image": "https://twitterscore.io/media/profiles/ethereum.jpg", "verified": true, "twitter_score": 912, "current_followers": 3654210, "followers_diff": -214, "description": "Ethereum is a global, open-source platform for decentralized applications.", "tags": [{"id": 11, "name": "Ethereum"}], "categories": [{"id": 1, "name": "Projects"}]}]}}}}, "default": {"$ref": "#/components/responses/Error"}}, "x-error-codes": ["api_key_missing", "api_key_invalid", "api_access_deactivated", "api_key_revoked", "method_not_allowed", "rate_limited", "quota_exceeded", "server_error"]}}, "/api/v1/get_trending": {"get": {"operationId": "get_trending", "summary": "Trending accounts ranked by Twitter Score or follower change over 3, 7 or 30 days", "description": "Returns the ranked list that powers the /trending/ page: non-suspended accounts whose Twitter Score was above zero at the start of the period, with current vs start-of-period follower counts and Twitter Score for the chosen window (3, 7 or 30 days) and the deltas between them. Rows come from a pre-aggregated, cron-refreshed slice of at most 1000 ranked rows per (days, category, sort, direction) combination, so `total` never exceeds 1000. Sort by Twitter Score change (`by=score`, default) or follower change (`by=followers`); ties are broken by the current Twitter Score. Restrict to one account category with `category_id` (ids from /api/v1/get_categories_list); `0` means every category, `-1` means accounts with no category. Accounts on the trending blacklist are removed before pagination. Each row carries the account's tags together with the tag-category each tag belongs to (`user_tags`). The response echoes the resolved `days` and `category_name` next to the pagination fields.\n**Nested fields**\n\n- `data[].twitter_id` (string): The account's numeric Twitter/X id as a string (CAST ... AS CHAR in the SQL view; preserves 64-bit precision).\n- `data[].name` (string): Display name.\n- `data[].username` (string): Handle without the @.\n- `data[].description` (string): Profile bio (empty string when none).\n- `data[].img` (string): Avatar URL (TwitterScore S3 copy, aws_image_url). When no image is stored the value is settings.MEDIA_URL + \"profiles/NoImageFound.png\": \"/media/profiles/NoImageFound.png\" on a deployment that serves media locally, or \"https://<bucket>.s3.amazonaws.com/media/profiles/NoImageFound.png\" when USE_S3 is on.\n- `data[].url` (string): Public TwitterScore profile page, https://twitterscore.io/twitter/{slug}.\n- `data[].curr_followers` (integer): Follower count at the end of the window (period snapshot).\n- `data[].prev_followers` (integer): Follower count at the start of the window.\n- `data[].twitter_score` (integer): Twitter Score at the end of the window (0-1000 scale, integer).\n- `data[].prev_twitter_score` (integer): Twitter Score at the start of the window; always > 0 because the SQL view requires prev_project_score > 0.\n- `data[].twitter_score_diff_int` (integer): twitter_score minus prev_twitter_score (snapshot score_diff); may be negative. Sort key for by=score.\n- `data[].twitter_score_diff_str` (string): twitter_score_diff_int formatted with a space as thousands separator, e.g. \"1 250\" or \"-32\".\n- `data[].followers_diff_int` (integer): curr_followers minus prev_followers; may be negative. Sort key for by=followers.\n- `data[].followers_diff_str` (string): followers_diff_int formatted with a space as thousands separator, e.g. \"14 135\".\n- `data[].user_tags` (array): Tags attached to the account. Empty array when the account has no tags.\n- `data[].user_tags[].id` (number): Tag id (see /api/v1/get_tags_list). Integral value, but may be serialised as a float such as 17.0 because the id column passes through a pandas DataFrame containing NULLs (see notes).\n- `data[].user_tags[].name` (string): Tag name.\n- `data[].user_tags[].categories_id` (number): Id of the tag-category the tag belongs to (same float caveat as id).\n- `data[].user_tags[].categories_name` (string): Name of the tag-category the tag belongs to.", "tags": ["Lists"], "security": [{"ApiKeyHeader": []}, {"BearerKey": []}, {"ApiKeyQuery": []}], "parameters": [{"name": "days", "in": "query", "required": false, "description": "Comparison window in days. Only 3, 7 or 30 are accepted. The value is first clamped to at most 30 (so 31, 100, ... become 30), then checked: any other integer (0, 1, 2, 4..29, negatives) returns {\"success\": false, \"message\": \"Days params is only available: 30|7|3\"}. A non-numeric value falls back to 30.", "schema": {"type": "integer", "enum": ["3", "7", "30"], "default": 30, "maximum": 30}, "example": 7}, {"name": "by", "in": "query", "required": false, "description": "Ranking column. `score` sorts by the Twitter Score change over the window (twitter_score_diff_int), `followers` by the follower change (followers_diff_int). Case-sensitive; any other value (including `Score`) returns {\"success\": false, \"message\": \"Sorting is only possible by [followers|score]\"}.", "schema": {"type": "string", "enum": ["score", "followers"], "default": "score"}, "example": "score"}, {"name": "sort", "in": "query", "required": false, "description": "Sort direction. `asc` or `ascending` (case-insensitive) sorts ascending (biggest losses first); anything else, including the default, sorts descending.", "schema": {"type": "string", "enum": ["desc", "asc", "descending", "ascending"], "default": "desc"}, "example": "desc"}, {"name": "category_id", "in": "query", "required": false, "description": "Account category filter. 0 = all categories, -1 = accounts without any category (reported as category_name \"NoCategory\"), otherwise a category id from /api/v1/get_categories_list. An unknown positive id returns {\"success\": false, \"message\": \"Category with id=N not found\"}. A non-numeric value falls back to 0.", "schema": {"type": "integer", "default": 0}, "example": 0}, {"name": "page", "in": "query", "required": false, "description": "1-based page number. Non-numeric, 0 or negative values are treated as 1. A page past the last one returns an empty `data` array.", "schema": {"type": "integer", "default": 1}, "example": 1}, {"name": "size", "in": "query", "required": false, "description": "Rows per page, capped at 100. Values below 1 are treated as 1; a non-numeric value falls back to 10.", "schema": {"type": "integer", "default": 10, "maximum": 100}, "example": 10}], "responses": {"200": {"description": "Success", "content": {"application/json": {"schema": {"type": "object", "properties": {"success": {"type": "boolean", "description": "true for a served page."}, "total": {"type": "integer", "description": "Number of ranked rows available for this days/category/sort combination after blacklist filtering; at most 1000. 0 while the cache is cold."}, "page": {"type": "integer", "description": "The page that was served (after clamping to 1)."}, "size": {"type": "integer", "description": "Number of items actually returned in `data` (less than the requested size on the last page)."}, "pages": {"type": "integer", "description": "Total number of pages at the requested size; at least 1 even when `total` is 0."}, "days": {"type": "integer", "description": "The comparison window that was applied (3, 7 or 30)."}, "category_name": {"type": "string", "description": "Name of the applied category: \"all\" for category_id=0, \"NoCategory\" for -1, otherwise the category's name."}}}, "example": {"success": true, "total": 1000, "page": 1, "size": 1, "pages": 1000, "days": 7, "category_name": "all", "data": [{"twitter_id": "295218901", "name": "vitalik.eth", "username": "VitalikButerin", "description": "mi pinxe lo crino tcati", "img": "https://twitterscore.s3.amazonaws.com/profiles/VitalikButerin.jpg", "url": "https://twitterscore.io/twitter/VitalikButerin", "curr_followers": 5812345, "prev_followers": 5798210, "twitter_score": 1000, "prev_twitter_score": 998, "twitter_score_diff_str": "2", "twitter_score_diff_int": 2, "followers_diff_int": 14135, "followers_diff_str": "14 135", "user_tags": [{"id": 17, "name": "Ethereum", "categories_id": 3, "categories_name": "Ecosystem"}]}]}}}}, "default": {"$ref": "#/components/responses/Error"}}, "x-error-codes": ["api_key_missing", "api_key_invalid", "api_access_deactivated", "api_key_revoked", "method_not_allowed", "rate_limited", "quota_exceeded", "server_error"]}}, "/api/v1/get_twitter_info": {"get": {"operationId": "get_twitter_info", "summary": "Profile fields, tags and categories of a single account", "description": "Resolves the account exactly like get_twitter_score (by `username` or `twitter_id`, with slug, previous-username and scored-twin fallbacks) and returns its profile fields plus TwitterScore's manual classification: `tags` (fine-grained labels such as a specific VC, ecosystem or sector) and `categories` (the coarse account type such as Founders, Projects, Venture Capitals). Tags and categories exist only on curated accounts (Account); for accounts known solely from the follower graph (Friend) both arrays are empty. The response does not include the Twitter Score or the twitter_id — use get_twitter_score for those.\n\n**Required:** one of `username`, `twitter_id`.\n**Nested fields**\n\n- `tags[].tag_id` (integer): Tag id; matches `tags[].id` from get_tags.\n- `tags[].tag_name` (string): Tag name.\n- `categories[].category_id` (integer): Category id; matches `categories[].id` from get_categories.\n- `categories[].category_name` (string): Category name.", "tags": ["Accounts"], "security": [{"ApiKeyHeader": []}, {"BearerKey": []}, {"ApiKeyQuery": []}], "parameters": [{"name": "username", "in": "query", "required": false, "description": "X/Twitter handle without the leading @ (whitespace/slashes stripped). Also matches the profile slug and previous handles of renamed accounts.", "schema": {"type": "string"}, "example": "VitalikButerin"}, {"name": "twitter_id", "in": "query", "required": false, "description": "Numeric X/Twitter user id. Tried before `username` when both are given.", "schema": {"type": "integer"}, "example": 295218901}], "responses": {"200": {"description": "Success", "content": {"application/json": {"schema": {"type": "object", "properties": {"success": {"type": "boolean", "description": "Always true on a successful lookup."}, "username": {"type": "string", "description": "Current handle of the resolved account."}, "name": {"type": ["string", "null"], "description": "Display name as shown on X."}, "description": {"type": "string", "description": "Profile bio text; empty string when the account has none (TextField default '', not null)."}, "followers_count": {"type": "integer", "description": "Follower count from TwitterScore's latest snapshot of the account (BaseAccount.current_followers)."}, "profile_image": {"type": ["string", "null"], "description": "Absolute URL of the profile picture stored on TwitterScore's S3 bucket (`https://<bucket>.s3.amazonaws.com/profiles/<file>`); null when no image is stored."}, "tags": {"type": "array", "description": "Manually assigned tags; empty for Friend-resolved accounts."}, "categories": {"type": "array", "description": "Account categories; empty for Friend-resolved accounts."}}}, "example": {"success": true, "username": "VitalikButerin", "name": "vitalik.eth", "description": "mi pinxe lo crino tcati", "followers_count": 5689412, "profile_image": "https://twitterscore-media.s3.amazonaws.com/profiles/VitalikButerin.jpg", "tags": [{"tag_id": 6, "tag_name": "Ethereum"}], "categories": [{"category_id": 2, "category_name": "Founders"}, {"category_id": 8, "category_name": "Influencers"}]}}}}, "default": {"$ref": "#/components/responses/Error"}}, "x-error-codes": ["api_key_missing", "api_key_invalid", "api_access_deactivated", "api_key_revoked", "invalid_params", "account_not_found", "method_not_allowed", "rate_limited", "quota_exceeded", "server_error"], "x-one-of-required": [["username", "twitter_id"]]}}, "/api/v1/get_twitter_score": {"get": {"operationId": "get_twitter_score", "summary": "Twitter Score of a single account", "description": "Resolves one X/Twitter account by `username` or `twitter_id` and returns its current Twitter Score (0–1000). Resolution order: by `twitter_id` in the curated accounts table (Account), then in the follower-graph table (Friend); by `username` as an exact match on Account.username, then Account.slug, then Friend.username, then Friend.slug, then a previous-username fallback for handles that were renamed away — so a request for an old handle returns the account's CURRENT `username` and `twitter_id`. If the resolved row has no score but a scored twin row with the same twitter_id exists, the twin's score is reported. When both parameters are sent, `twitter_id` is tried first and `username` is used only if the id resolves nothing.\n\n**Required:** one of `username`, `twitter_id`.", "tags": ["Scores"], "security": [{"ApiKeyHeader": []}, {"BearerKey": []}, {"ApiKeyQuery": []}], "parameters": [{"name": "username", "in": "query", "required": false, "description": "X/Twitter handle without the leading @ (surrounding whitespace and slashes are stripped; an '@' prefix is NOT stripped). Exact match on the stored handle (case-insensitive only insofar as the MySQL column collation is case-insensitive); also matches the TwitterScore profile slug and previous handles of renamed accounts.", "schema": {"type": "string"}, "example": "VitalikButerin"}, {"name": "twitter_id", "in": "query", "required": false, "description": "Numeric X/Twitter user id. Tried before `username` when both are given.", "schema": {"type": "integer"}, "example": 295218901}], "responses": {"200": {"description": "Success", "content": {"application/json": {"schema": {"type": "object", "properties": {"success": {"type": "boolean", "description": "Always true on a successful lookup."}, "username": {"type": "string", "description": "Current handle of the resolved account (may differ from the requested one if it was resolved through a slug or a previous username)."}, "twitter_id": {"type": "string", "description": "Numeric X/Twitter id serialised as a string via str() (ids exceed 2^53, so they are never emitted as JSON numbers)."}, "twitter_score": {"type": "number", "description": "Twitter Score on the 0–1000 scale. Account/Friend.project_score is a FloatField (DOUBLE), so it is emitted as a float (e.g. 1000.0); integer-valued in practice."}}}, "example": {"success": true, "username": "VitalikButerin", "twitter_id": "295218901", "twitter_score": 1000.0}}}}, "default": {"$ref": "#/components/responses/Error"}}, "x-error-codes": ["api_key_missing", "api_key_invalid", "api_access_deactivated", "api_key_revoked", "invalid_params", "account_not_found", "method_not_allowed", "rate_limited", "quota_exceeded", "server_error"], "x-one-of-required": [["username", "twitter_id"]]}}, "/api/v1/get_twitter_scores_diff": {"get": {"operationId": "get_twitter_scores_diff", "summary": "Change of an account's Twitter Score over the last 7 and 30 days", "description": "Returns how the account's Twitter Score moved over the trailing week and month, computed from TwitterScore's daily score snapshots: for each window, diff = newest snapshot − oldest snapshot whose date is on or after (today_UTC − 7 days) / (today_UTC − 30 days), ignoring snapshots with a zero score. `date` is the date of the oldest snapshot used as the baseline, `diff_str` is the same number as a signed string for display (\"+12\", \"-3\", \"0\"). When no snapshot exists in the window — including accounts known only from the follower graph, which have no snapshots — diff is 0 and date is null. `today` is the UTC date on which the response was computed.\n\n**Required:** one of `username`, `twitter_id`.\n**Nested fields**\n\n- `week.date` (string (date, YYYY-MM-DD)): Date of the oldest snapshot in the 7-day window (the baseline); null when there is no snapshot.\n- `week.diff` (integer): Newest snapshot score minus baseline score within the window; 0 when no data.\n- `week.diff_str` (string): `diff` as a signed string: \"+N\" for gains, \"-N\" for losses, \"0\" for no change / no data.\n- `month.date` (string (date, YYYY-MM-DD)): Date of the oldest snapshot in the 30-day window; null when there is no snapshot.\n- `month.diff` (integer): Newest snapshot score minus baseline score within the window; 0 when no data.\n- `month.diff_str` (string): `diff` as a signed string.", "tags": ["Scores"], "security": [{"ApiKeyHeader": []}, {"BearerKey": []}, {"ApiKeyQuery": []}], "parameters": [{"name": "username", "in": "query", "required": false, "description": "X/Twitter handle without the leading @ (whitespace/slashes stripped). Also matches the profile slug and previous handles of renamed accounts.", "schema": {"type": "string"}, "example": "VitalikButerin"}, {"name": "twitter_id", "in": "query", "required": false, "description": "Numeric X/Twitter user id. Tried before `username` when both are given.", "schema": {"type": "integer"}, "example": 295218901}], "responses": {"200": {"description": "Success", "content": {"application/json": {"schema": {"type": "object", "properties": {"success": {"type": "boolean", "description": "Always true on a successful lookup."}, "today": {"type": "string (date, YYYY-MM-DD)", "description": "UTC date when the response was computed (may lag up to 1 hour around midnight because of the result cache)."}, "week": {"type": "object", "description": "Score change over snapshots dated within the last 7 days."}, "month": {"type": "object", "description": "Score change over snapshots dated within the last 30 days."}}}, "example": {"success": true, "today": "2026-10-03", "week": {"date": "2026-09-26", "diff": 0, "diff_str": "0"}, "month": {"date": "2026-09-03", "diff": 3, "diff_str": "+3"}}}}}, "default": {"$ref": "#/components/responses/Error"}}, "x-error-codes": ["api_key_missing", "api_key_invalid", "api_access_deactivated", "api_key_revoked", "invalid_params", "account_not_found", "method_not_allowed", "rate_limited", "quota_exceeded", "server_error"], "x-one-of-required": [["username", "twitter_id"]]}}, "/api/v1/get_twitter_top_followers": {"get": {"operationId": "get_twitter_top_followers", "summary": "Top 5 followers of an account ranked by Twitter Score", "description": "Returns the five highest-scoring accounts that follow the given profile. Only followers that TwitterScore actively monitors are considered: the follower must be an active, non-suspended monitored account and the follow relationship must still be active. Results are sorted by the follower's Twitter Score descending and hard-capped at 5 entries; there is no paging. Deprecated in the public docs in favour of the paginated follower list (get_followers / top_followers_paginate), which returns the same accounts with richer fields.\n\n**Required:** one of `username`, `twitter_id`.\n**Nested fields**\n\n- `top_followers[].twitter_id` (integer): Follower's numeric X id. This endpoint returns it as a JSON number (PositiveBigIntegerField), unlike the paginated endpoints which return it as a string; values above 2^53 lose precision in JavaScript.\n- `top_followers[].username` (string): Follower's handle without @.\n- `top_followers[].name` (string): Follower's display name.\n- `top_followers[].twitter_score` (number): Follower's Twitter Score on the 0–1000 scale (FloatField, so it may carry decimals).\n- `top_followers[].followers_count` (integer): Follower's own current follower count.\n- `top_followers[].profile_image` (string): Absolute URL of the follower's stored profile picture (S3 storage), or null when none is stored.", "tags": ["Followers"], "security": [{"ApiKeyHeader": []}, {"BearerKey": []}, {"ApiKeyQuery": []}], "parameters": [{"name": "username", "in": "query", "required": false, "description": "X/Twitter handle of the account whose followers to list, without the leading @. Surrounding whitespace and a trailing slash are tolerated. Renamed handles are resolved through the previous-username history when the current handle is unknown.", "schema": {"type": "string"}, "example": "VitalikButerin"}, {"name": "twitter_id", "in": "query", "required": false, "description": "Numeric X/Twitter user id of the account. Tried first when both are sent; if no account matches the id, the lookup falls back to username. Can exceed 2^53 for newer accounts, so treat it as a 64-bit integer.", "schema": {"type": "integer"}, "example": 295218901}], "responses": {"200": {"description": "Success", "content": {"application/json": {"schema": {"type": "object", "properties": {"success": {"type": "boolean", "description": "true on success."}, "top_followers": {"type": "array", "description": "Up to 5 follower objects, highest Twitter Score first. Empty array when the account has no monitored followers."}}}, "example": {"success": true, "top_followers": [{"twitter_id": 44196397, "username": "elonmusk", "name": "Elon Musk", "twitter_score": 998.6, "followers_count": 221574312, "profile_image": "https://twitterscore.s3.amazonaws.com/media/profiles/elonmusk.jpg"}, {"twitter_id": 902926941413453824, "username": "cz_binance", "name": "CZ BNB", "twitter_score": 981.2, "followers_count": 10215733, "profile_image": "https://twitterscore.s3.amazonaws.com/media/profiles/cz_binance.jpg"}, {"twitter_id": 14379660, "username": "brian_armstrong", "name": "Brian Armstrong", "twitter_score": 964.9, "followers_count": 1489021, "profile_image": null}]}}}}, "default": {"$ref": "#/components/responses/Error"}}, "x-error-codes": ["api_key_missing", "api_key_invalid", "api_access_deactivated", "api_key_revoked", "invalid_params", "account_not_found", "method_not_allowed", "rate_limited", "quota_exceeded", "server_error"], "deprecated": true, "x-one-of-required": [["username", "twitter_id"]]}}, "/api/v1/limits": {"get": {"operationId": "limits", "summary": "Current plan limits and monthly usage for the calling API account", "description": "Returns the limits attached to the API key's account and how much of the monthly quota has already been used. Usage is account-scoped: all named keys of one account share a single monthly counter, so this endpoint reports the same figures whichever of the account's keys is used. The response is never cached, and the call itself is counted against the monthly quota (the counter is incremented before the view runs, so `used` already includes this request). Unlike every other endpoint in this set, the success body has no `success` key: the top-level keys are `user_id` and `rates`. `rates` always has two entries: the first describes the monthly quota (total/used/remaining and when the 30-day counter window resets), the second describes the per-minute request rate (`total` requests per `period`).\n**Nested fields**\n\n- `rates[].period` (string): Window of the limit. First entry: always \"month\". Second entry: the unit parsed from the plan's rate string (\"second\", \"minute\", \"hour\" or \"day\", falling back to \"minute\" for an unknown suffix); in practice \"minute\".\n- `rates[].total` (integer): Limit for the window: requests per month (first entry, ProfileAPIKeyModel.monthly, default 10000) or requests per rate window (second entry, parsed from ProfileAPIKeyModel.rate, default '1000/m').\n- `rates[].used` (integer): Requests already counted in the current monthly window, including this call. Present only in the first (month) entry.\n- `rates[].remaining` (integer): total − used for the monthly window (can go to 0; the enforcer blocks once used >= total). Present only in the first (month) entry.\n- `rates[].reset_time_at` (string (ISO 8601 date-time, naive server-local time, milliseconds, no timezone suffix)): Moment when the current 30-day monthly counter expires and usage returns to 0. Present only in the first (month) entry.", "tags": ["Accounts"], "security": [{"ApiKeyHeader": []}, {"BearerKey": []}, {"ApiKeyQuery": []}], "parameters": [], "responses": {"200": {"description": "Success", "content": {"application/json": {"schema": {"type": "object", "properties": {"user_id": {"type": "integer", "description": "Internal TwitterScore user id that owns the API key (the plan holder)."}, "rates": {"type": "array", "description": "Exactly two entries: [0] the monthly quota, [1] the per-minute rate limit."}}}, "example": {"user_id": 48213, "rates": [{"period": "month", "total": 10000, "used": 1327, "remaining": 8673, "reset_time_at": "2026-10-28T09:14:52.318"}, {"period": "minute", "total": 1000}]}}}}, "default": {"$ref": "#/components/responses/Error"}}, "x-error-codes": ["api_key_missing", "api_key_invalid", "api_access_deactivated", "api_key_revoked", "method_not_allowed", "rate_limited", "quota_exceeded", "server_error"]}}, "/api/v1/top_followers_paginate": {"get": {"operationId": "top_followers_paginate", "summary": "Paginated list of an account's followers ranked by Twitter Score", "description": "Returns the monitored followers of a profile, highest Twitter Score first, in pages. Each entry carries the follower's score, follower count, description, tags, categories and the date TwitterScore first recorded the follow. The follower set is the same as get_twitter_top_followers (active, non-suspended monitored accounts with an active follow relationship), but without the 5-entry cap. Deprecated in the public docs in favour of get_followers, which offers the same data with filters.\n\n**Required:** one of `username`, `twitter_id`.\n**Nested fields**\n\n- `top_followers[].twitter_id` (string): Follower's numeric X id as a string (safe for 64-bit ids).\n- `top_followers[].username` (string): Follower's handle without @.\n- `top_followers[].name` (string): Follower's display name.\n- `top_followers[].description` (string): Follower's profile bio (empty string when none).\n- `top_followers[].twitter_score` (number): Follower's Twitter Score on the 0–1000 scale.\n- `top_followers[].followers_count` (integer): Follower's own current follower count.\n- `top_followers[].profile_image` (string): Absolute URL of the follower's stored profile picture, or null.\n- `top_followers[].tags` (array): Tags assigned to the follower by TwitterScore (e.g. Tier 1 VC).\n- `top_followers[].tags[].id` (integer): Tag id (matches get_tags).\n- `top_followers[].tags[].name` (string): Tag name.\n- `top_followers[].categories` (array): Categories assigned to the follower (e.g. Projects, Influencers).\n- `top_followers[].categories[].id` (integer): Category id (matches get_categories).\n- `top_followers[].categories[].name` (string): Category name.\n- `top_followers[].subscribed_at` (string): ISO 8601 UTC datetime when TwitterScore first recorded this follow relationship. This is the detection time in TwitterScore's database, not the moment the follow happened on X.", "tags": ["Followers"], "security": [{"ApiKeyHeader": []}, {"BearerKey": []}, {"ApiKeyQuery": []}], "parameters": [{"name": "username", "in": "query", "required": false, "description": "X/Twitter handle of the account whose followers to list, without the leading @. Surrounding whitespace and a trailing slash are tolerated; renamed handles are resolved via previous-username history.", "schema": {"type": "string"}, "example": "VitalikButerin"}, {"name": "twitter_id", "in": "query", "required": false, "description": "Numeric X/Twitter user id of the account. Tried first when both are sent; falls back to username if the id resolves nothing.", "schema": {"type": "integer"}, "example": 295218901}, {"name": "page", "in": "query", "required": false, "description": "1-based page number. Non-numeric, 0 or negative values are treated as 1. A page beyond the last returns an empty list with the same metadata.", "schema": {"type": "integer", "default": 1}, "example": 1}, {"name": "size", "in": "query", "required": false, "description": "Items per page, capped at 25. Non-numeric values fall back to 5; 0 or negative values are floored to 1.", "schema": {"type": "integer", "default": 5, "maximum": 25}, "example": 10}], "responses": {"200": {"description": "Success", "content": {"application/json": {"schema": {"type": "object", "properties": {"success": {"type": "boolean", "description": "true on success."}, "total": {"type": "integer", "description": "Total number of monitored followers matching the query, across all pages."}, "page": {"type": "integer", "description": "The page that was served (after normalisation to >= 1)."}, "size": {"type": "integer", "description": "Number of items actually returned on this page (may be smaller than the requested size on the last page, 0 past the end)."}, "pages": {"type": "integer", "description": "Total number of pages for the requested size; always at least 1 even when total is 0."}, "top_followers": {"type": "array", "description": "Follower objects for this page, highest Twitter Score first."}}}, "example": {"success": true, "total": 48213, "page": 1, "size": 2, "pages": 24107, "top_followers": [{"twitter_id": "44196397", "username": "elonmusk", "name": "Elon Musk", "description": "", "twitter_score": 998.6, "followers_count": 221574312, "profile_image": "https://twitterscore.s3.amazonaws.com/media/profiles/elonmusk.jpg", "tags": [{"id": 7, "name": "Key Opinion Leader"}], "categories": [{"id": 2, "name": "Influencers"}], "subscribed_at": "2022-11-14T03:21:08.512Z"}, {"twitter_id": "902926941413453824", "username": "cz_binance", "name": "CZ BNB", "description": "Co-Founder & Former CEO @binance", "twitter_score": 981.2, "followers_count": 10215733, "profile_image": "https://twitterscore.s3.amazonaws.com/media/profiles/cz_binance.jpg", "tags": [{"id": 3, "name": "CEX"}], "categories": [{"id": 5, "name": "Founders"}, {"id": 2, "name": "Influencers"}], "subscribed_at": "2021-06-02T19:44:51.003Z"}]}}}}, "default": {"$ref": "#/components/responses/Error"}}, "x-error-codes": ["api_key_missing", "api_key_invalid", "api_access_deactivated", "api_key_revoked", "invalid_params", "account_not_found", "method_not_allowed", "rate_limited", "quota_exceeded", "server_error"], "deprecated": true, "x-one-of-required": [["username", "twitter_id"]]}}}, "components": {"securitySchemes": {"ApiKeyHeader": {"type": "apiKey", "in": "header", "name": "X-API-Key"}, "BearerKey": {"type": "http", "scheme": "bearer", "description": "The same 32-character API key as a Bearer token."}, "ApiKeyQuery": {"type": "apiKey", "in": "query", "name": "api_key", "description": "Original form, kept for compatibility. Prefer X-API-Key: keys in URLs leak into logs and referers."}}, "schemas": {"ErrorDetail": {"type": "object", "properties": {"code": {"type": "string", "enum": ["api_key_missing", "api_key_invalid", "api_access_deactivated", "api_key_revoked", "invalid_params", "account_not_found", "method_not_allowed", "rate_limited", "quota_exceeded", "server_error"]}, "message": {"type": "string"}, "docs_url": {"type": "string", "format": "uri"}}, "required": ["code", "message"]}, "ErrorResponse": {"type": "object", "properties": {"success": {"type": "boolean", "const": false}, "message": {"type": "string", "description": "Legacy human-readable message (same text as error.message)."}, "error": {"$ref": "#/components/schemas/ErrorDetail"}}, "required": ["success", "message", "error"]}}, "responses": {"Error": {"description": "Error. HTTP 200 during the compatibility period, then 400/401/403/404/405/429/500 by error.code.", "headers": {"Retry-After": {"description": "Seconds until the per-minute window resets (rate_limited only).", "schema": {"type": "integer"}}, "WWW-Authenticate": {"description": "Present on 401 once strict statuses are enabled.", "schema": {"type": "string"}}}, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorResponse"}, "example": {"success": false, "message": "API key has to be provided", "error": {"code": "api_key_missing", "message": "API key has to be provided", "docs_url": "https://twitterscore.io/developers/errors/"}}}}}}}}