Skip to content

Credentials

FxEmbed works best with X/Twitter account credentials to fetch post data from the Twitter API. Without them, you will have lower rate limits and not be able to fetch NSFW posts.

Credentials are defined in a credentials.json file:

{
"twitter": {
"accounts": [
{
"authToken": "your_auth_token",
"csrfToken": "your_ct0_csrf",
"username": "account_screen_name"
}
]
}
}

You can include multiple accounts in the accounts array. FxEmbed will use them to distribute requests.

The authToken and csrfToken values come from an authenticated X/Twitter web session. You can extract them from your browser’s cookies:

  • authToken: The auth_token cookie
  • csrfToken: The ct0 cookie or X-Csrf-Token header

FxEmbed includes tooling to encrypt, push, and pull credentials:

Encrypt your credentials.json using your CREDENTIAL_KEY:

Terminal window
npm run credentials:encrypt

Upload encrypted credentials to Cloudflare R2 storage:

Terminal window
npm run credentials:push

Combined encrypt + push in one step:

Terminal window
npm run credentials:encrypt-push

Download and decrypt credentials from R2:

Terminal window
npm run credentials:pull

Refresh CSRF tokens for existing credentials:

Terminal window
npm run credentials:add-csrf

Remove sensitive data from the credentials file:

Terminal window
npm run credentials:strip

The encryption key must be set as a Wrangler secret so the deployed worker can decrypt credentials at runtime:

Terminal window
wrangler secret put CREDENTIAL_KEY

FxEmbed by default will use the public Bluesky AppView API. We support specifying fallback accounts on different PDSes in the event of downtime of the public API. We will always prefer the public API unless it is down.

The proxy uses standard Bluesky app passwords

Bluesky accounts live alongside Twitter accounts in the same credentials.json:

{
"bluesky": {
"accounts": [
{
"identifier": "your.handle.bsky.social",
"appPassword": "xxxx-xxxx-xxxx-xxxx",
"service": "https://bsky.social"
}
]
}
}

Fields:

  • identifier: The account’s handle (e.g. your.handle.bsky.social) or DID.
  • appPassword: An app password generated at bsky.app/settings/app-passwords. Do not use the account’s main password.
  • service: The base URL of the account’s PDS. For accounts hosted on Bluesky’s infrastructure, use https://bsky.social (the entryway will route to the correct *.host.bsky.network PDS automatically). For self-hosted or third-party PDSes, set this to the actual PDS URL (e.g. https://pds.example.com).
  1. Sign in to the account you want to use as a proxy.
  2. Open Settings → Privacy and security → App passwords (or visit bsky.app/settings/app-passwords directly).
  3. Click Add App Password, give it a descriptive name (e.g. FxEmbed Proxy), and copy the generated xxxx-xxxx-xxxx-xxxx value.
  4. Paste it into the appPassword field above.

App passwords can be revoked at any time from the same screen if a key is ever compromised.

Bluesky credentials are encrypted, pushed, and pulled using the exact same credential management scripts and CREDENTIAL_KEY as Twitter — there’s nothing Bluesky-specific to configure on the worker side.

Instagram’s logged-out surfaces are heavily restricted: follower lists, likers, search, tagged posts and stories return nothing at all, and post and profile lookups are rate limited and gated on anything age-restricted. Adding an Instagram account proxy unlocks those surfaces and makes the existing ones far more reliable.

Without it, FxEmbed still serves Instagram posts, profiles, profile grids and comments over the logged-out web path. The proxy-only routes report HTTP 501 rather than an empty result, so you can tell “not configured” apart from “this account really has no followers”.

Instagram accounts live alongside Twitter and Bluesky accounts in the same credentials.json:

{
"instagram": {
"accounts": [
{
"sessionId": "your_sessionid_cookie",
"userId": "your_ds_user_id_cookie",
"csrfToken": "your_csrftoken_cookie",
"mid": "your_mid_cookie",
"deviceId": "your_ig_did_cookie",
"username": "account_handle",
"platform": "web",
"androidDeviceId": "android-0123456789abcdef"
}
]
}
}

Fields:

  • sessionId (required): The sessionid cookie of a logged-in Instagram session.
  • userId: The ds_user_id cookie — the account’s numeric pk.
  • csrfToken: The csrftoken cookie.
  • mid / deviceId: The mid and ig_did cookies. Optional, but Instagram is happier when the cookie jar looks complete.
  • username: Used only for logging, so you can tell which session got rate limited.
  • platform: web (default) or android. This picks the client fingerprint the proxy presents — see below.
  • androidDeviceId: Optional. Sent as X-IG-Device-ID when platform is android. Use android- followed by 16 hex characters (for example android-0123456789abcdef). Ignored for web sessions.

Only sessionId is strictly required; everything else improves how ordinary the session looks.

platform must match where the sessionid came from:

  • web — a cookie harvested from www.instagram.com in a desktop browser. FxEmbed sends the matching desktop Chrome User-Agent, web app id, and browser Sec-Fetch-*/Origin headers.
  • android — a cookie harvested from the Instagram Android app. FxEmbed sends the app’s own User-Agent, app id, and X-IG-Device-ID (from androidDeviceId when set), and omits the browser-only headers.

Mixing the two is the usual cause of an unexpected checkpoint, so keep this consistent with where you got the cookie.

  1. Sign in to the account you want to use as a proxy in a browser.
  2. Open DevTools → Application → Cookies → https://www.instagram.com.
  3. Copy the sessionid, ds_user_id, csrftoken, mid and ig_did values into the fields above, leaving platform as web.

Sessions are long-lived but not permanent: logging the account out, changing its password, or an Instagram-side checkpoint invalidates the cookie. FxEmbed rotates to the next configured account on 401, 403 and 429, on an HTML login page, and on a 200 { status: 'fail' } body (checkpoint / spam block), so a stale entry degrades one account rather than the whole deployment. Use accounts you’re willing to lose, not a personal one.

Instagram credentials are encrypted, pushed, and pulled using the exact same credential management scripts and CREDENTIAL_KEY as Twitter and Bluesky. npm run credentials:strip keeps only the cookie-jar fields, so a credentials.complete.json that also holds a login password will not leak it into the encrypted bundle.

Threads accounts are Instagram accounts, so there is no separate credential block: the same instagram.accounts pool powers Threads. FxEmbed only swaps the client fingerprint — the Threads (Barcelona) app id and User-Agent — when it calls a Threads endpoint.

Logged-out threads.com is even more restricted than Instagram’s: search, typeahead, trending topics, likers, follow lists, and the Replies / Reposts / Media profile tabs are all behind a login. With a proxy configured those become available at /2/threads/…; without one they report 501, exactly like their Instagram counterparts. Single posts, profiles, profile timelines and conversations keep working either way — the proxy just gives fuller and more reliable results.