ft's media parser at src/graphql-bookmarks.ts:272-288 pulls seven fields from each media object: type, url, expandedUrl, width, height, altText, videoVariants. It drops media.features.all.tags — the list of accounts an author tagged in a photo, which X renders as overlay chips below the media on x.com.
For affiliate/maker/review/product tweets where the relevant brand or person is tagged on the photo rather than @-mentioned in the text, this is the only signal that the account is associated with the bookmark. Without it, downstream consumers can't surface the relationship: text parsing finds nothing, the bookmark's @-mentions array is empty, the bio-mention path is author-level not tweet-level.
Field shape (verified May 2026)
Each tagged user is an object on media.features.all.tags[]:
{
"name": "<display name>",
"screen_name": "<@handle>",
"type": "user",
"user_id": "<numeric id>"
}
Path: legacy.extended_entities.media[i].features.all.tags[] — the same media array ft already iterates at line 272. Same path applies to quoted-tweet media at quoted_status_result.result.legacy.extended_entities.media[i].features.all.tags[].
Photo-only — videos use a different shape
Verified across three sampled tweets containing tagged media (one outer photo, one quoted photo, one quoted video):
- Photos carry
features.all.tags[] populated when accounts are tagged. They also carry features.{small,medium,large,orig}.faces[] for face-crop detection — unrelated to user tagging.
- Videos don't have a
features field at all. They expose source_user_id_str and source_status_id_str instead, which mark the original poster when a video has been reposted (attribution-to-creator, not tagged-users). Different concept; out of scope for this issue.
So this issue is specifically about photo tagging. If X supports video tagging it's in a separate code path with separate field shape.
Suggested scope
Extend the mediaObjects.map block at src/graphql-bookmarks.ts:276-288 to also extract features.all.tags. Extend BookmarkMediaObject (src/types.ts:7-19) with a new optional field.
Roughly:
// types.ts — add to BookmarkMediaObject
taggedUsers?: { name: string; screenName: string; userId: string }[];
// graphql-bookmarks.ts — add inside the mediaObjects.map at ~L287
taggedUsers: (m.features?.all?.tags ?? [])
.filter((t: any) => t?.type === 'user')
.map((t: any) => ({ name: t.name, screenName: t.screen_name, userId: t.user_id })),
Naming, exact field shape, persistence approach — all maintainer's choice. If mediaObjects is stored as a JSON column (same pattern as quoted_tweet_json), adding the field is automatic — no schema migration. If individual columns, this would add one per-media-item TEXT column or similar.
The type === "user" filter is defensive; in all three sampled responses every tag entry had type: "user", but X may add other tag types (locations? products?) and downstream consumers presumably only want user-tags.
Why this matters for downstream
A common pattern on X: investor/maker/review tweets where the relevant brand or maker account is tagged under the photo rather than typed into the body. For these tweets, the photo-tag is the only signal that the bookmark is "about" that account. Without surfacing the tag in ft's data:
- Search by handle doesn't find the bookmark (handle isn't in
text)
- Filter-by-account dimension doesn't include the tagged account
- GUIs can't render the tag chips X displays natively below the media
- The relationship "this tweet is about @x" is lost even though X surfaces it visually
ft's media parser at
src/graphql-bookmarks.ts:272-288pulls seven fields from each media object:type,url,expandedUrl,width,height,altText,videoVariants. It dropsmedia.features.all.tags— the list of accounts an author tagged in a photo, which X renders as overlay chips below the media on x.com.For affiliate/maker/review/product tweets where the relevant brand or person is tagged on the photo rather than @-mentioned in the text, this is the only signal that the account is associated with the bookmark. Without it, downstream consumers can't surface the relationship: text parsing finds nothing, the bookmark's
@-mentionsarray is empty, the bio-mention path is author-level not tweet-level.Field shape (verified May 2026)
Each tagged user is an object on
media.features.all.tags[]:{ "name": "<display name>", "screen_name": "<@handle>", "type": "user", "user_id": "<numeric id>" }Path:
legacy.extended_entities.media[i].features.all.tags[]— the samemediaarray ft already iterates at line 272. Same path applies to quoted-tweet media atquoted_status_result.result.legacy.extended_entities.media[i].features.all.tags[].Photo-only — videos use a different shape
Verified across three sampled tweets containing tagged media (one outer photo, one quoted photo, one quoted video):
features.all.tags[]populated when accounts are tagged. They also carryfeatures.{small,medium,large,orig}.faces[]for face-crop detection — unrelated to user tagging.featuresfield at all. They exposesource_user_id_strandsource_status_id_strinstead, which mark the original poster when a video has been reposted (attribution-to-creator, not tagged-users). Different concept; out of scope for this issue.So this issue is specifically about photo tagging. If X supports video tagging it's in a separate code path with separate field shape.
Suggested scope
Extend the
mediaObjects.mapblock atsrc/graphql-bookmarks.ts:276-288to also extractfeatures.all.tags. ExtendBookmarkMediaObject(src/types.ts:7-19) with a new optional field.Roughly:
Naming, exact field shape, persistence approach — all maintainer's choice. If
mediaObjectsis stored as a JSON column (same pattern asquoted_tweet_json), adding the field is automatic — no schema migration. If individual columns, this would add one per-media-item TEXT column or similar.The
type === "user"filter is defensive; in all three sampled responses every tag entry hadtype: "user", but X may add other tag types (locations? products?) and downstream consumers presumably only want user-tags.Why this matters for downstream
A common pattern on X: investor/maker/review tweets where the relevant brand or maker account is tagged under the photo rather than typed into the body. For these tweets, the photo-tag is the only signal that the bookmark is "about" that account. Without surfacing the tag in ft's data:
text)