# Bluesky Brand Presence Mapper - Followers for Clay (`mambalabs/bluesky-brand-presence-mapper`) Actor

Resolves a company domain to its official Bluesky account and returns exact follower, following and post counts through the public AT Protocol API. Flat, Clay ready, one row per company. Counts are exact, not rounded.

- **URL**: https://apify.com/mambalabs/bluesky-brand-presence-mapper.md
- **Developed by:** [Mamba Labs](https://apify.com/mambalabs) (community)
- **Categories:** Lead generation, Automation, Social media
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.45 / 1,000 company checkeds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

### 🔎 What can Bluesky Brand Presence Mapper do?

Give it a company domain. It finds that company's official Bluesky account and returns the profile URL plus **exact** follower, following and post counts, in one flat row per company.

It uses Bluesky's own public AT Protocol API, which is public by design rather than by oversight. No login, no API key, no scraping, no browser.

| 📦 What you get | ⚙️ Features and integrations |
|---|---|
| 🦋 **Brand Bluesky account**, resolved from a company domain<br>🔢 **Exact follower, following and post counts**, never rounded<br>🪪 **Stable DID**, plus domain verification status<br>🧾 **18 flat fields**, `snake_case`, one row per company | 🔗 **Domain handle first**, which is how most brands verify on Bluesky<br>📡 **Public AT Protocol API**, documented and keyless<br>🕰️ **Account creation date**, so you can tell new from established<br>⬇️ **Export** to JSON, CSV, Excel, HTML or XML |

Bought by teams tracking brand migration off X, media and publishing prospectors, and developer relations teams mapping where technical audiences moved.

> 🚫 **This is not a Bluesky firehose or post scraper.** It returns one account level row per company. It does not stream posts, replies or the timeline. Bluesky adoption among B2B companies is still thin, so expect `not_found` often, and expect it to be correct.

### 💡 Why use Bluesky Brand Presence Mapper?

**The counts are exact, and almost nowhere else are they.** TikTok serves 2,000,000. Instagram serves 3M. YouTube serves "902 thousand subscribers". Those are the platforms rounding, not us, and summing them across a target list sums the rounding. Bluesky serves 1,204 and means 1,204.

**Domain verification is a free identity check.** Bluesky lets a company use its own domain as its handle, but only after a DNS check the company must pass. So when `bluesky_handle` equals `bluesky_domain_verified` is true, you are not looking at a plausible match, you are looking at an account the company proved it controls. No other platform in this family offers that.

**It starts from a domain.** Every Bluesky tool on the Store starts from a handle, which is the thing you do not have. If you had the handle you would not need the tool.

#### 🧭 Four routes, cheapest first

1. **The domain as a handle.** A company that verified its domain IS reachable at that handle. One API call, no discovery, exact answer.
2. **The company homepage.** Its own `sameAs` block or footer links, read directly with no proxy.
3. **Bluesky account search.** The platform's own search, filtered through an identity gate so a similarly named account is dropped rather than shipped.
4. **A web search**, last, and only for what the first three missed.

The row tells you which route answered, in `bluesky_discovery`. If your use case cannot carry a weak match, filter to `input_handle`, `homepage_sameas` and `homepage_link` and drop the rest.

### 📋 What data can Bluesky Brand Presence Mapper extract?

Every row carries **18 flat fields**. These are the ones a buyer
actually filters and sorts on.

| Field | Type | Meaning |
|---|---|---|
| `degraded` | boolean | True when this row could not be produced normally, for example the company site was unreachable and no discovery could run. A degraded row is never charged. |
| `degradation_reason` | string | null | Why the row is degraded, in plain words. Null on a normal row. |
| `company_domain` | string | null | The company domain this row is about, normalized. Null when only a handle or a name was supplied. This is the join key across the whole Mamba Labs fleet. |
| `company_name` | string | null | The company name as supplied or derived. Improves search accuracy and is what the identity gate matches against. |
| `bluesky_url` | string | null | Canonical Bluesky profile URL, or null when no trustworthy account was found. |
| `bluesky_handle` | string | null | The resolved handle. When this equals the company domain, the company has verified that domain with Bluesky, which is the strongest identity evidence this platform offers. |
| `bluesky_did` | string | null | The account decentralized identifier. Unlike a handle this never changes, so it is the field to store if you are tracking one account over time. |
| `bluesky_domain_verified` | boolean | null | True when the resolved handle IS the company domain, which Bluesky only permits after a DNS check. False when the account was found some other way. Null when no account was found. |
| `bluesky_followers` | integer | null | Exact follower count. Bluesky serves the real integer, so unlike every other platform in this family this number can be summed across a list. |
| `bluesky_following` | integer | null | Exact count of accounts this account follows. |
| `bluesky_posts` | integer | null | Exact count of posts on the account. Zero is a real answer and means a registered but unused account, which is different from null. |
| `bluesky_followers_exact` | boolean | null | Always true when a count was returned. Bluesky does not round. The field exists so this actor row is shape compatible with the rest of the family, where rounding is the norm. |
| `bluesky_display_name` | string | null | The account display name as set by its owner. |
| `bluesky_bio` | string | null | The account description text. |
| `bluesky_created_at` | string | null | When the account was created, ISO 8601. Useful for telling an established presence from one opened last week. |
| `bluesky_discovery` | string | null | How the account was found: domain\_handle (the company domain is the verified handle, strongest), input\_handle, homepage\_sameas, homepage\_link, search, or pattern\_guess. Anything below homepage\_link is weaker evidence and you may want to filter on it. |
| `bluesky_status` | string | ok, not\_found, not\_extractable, blocked, identity\_mismatch, auth\_failed or skipped. Read this before reading any count. |
| `run_date` | string | ISO 8601 timestamp of this run. Social counts move, so a row without a date is a number with no shelf life. |

> ⚠️ **How to read these values.** `bluesky_did` is the field to store if you are tracking an account over time. A handle can be changed by its owner; a DID cannot.

### 🛠️ How to find a company's Bluesky account

1. Put a company domain in `company_domain`. That is the whole setup.
2. Optionally add `company_name`, which is what the identity gate matches a discovered account against. Supplying it reduces wrong matches.
3. If you already know the handle, put it in `handle` and the actor skips discovery and goes straight to the fetch, which is cheaper.
4. For a list, pass an array of objects with the same fields.

#### 🧪 Using it in Clay

Add an **Enrichment > Apify** column, pick this actor, and map `company_domain` to your domain column. Every output field arrives as its own flat column, snake\_case, no nesting, so there is nothing to unwrap.

Save credits by gating the column: run it only on rows where your ICP filter already passed. There is no point resolving a Bluesky account for a company you are not going to contact.

### 💵 How much does it cost?

Pay per event. You are charged for output, never for input.

| Event | Fires when | Price |
|---|---|---|
| `company-checked` | Once per company for which the discovery cascade completed and a non degraded row was produced, whether or not a Bluesky profile was found. Does not fire on a degraded row, because on a degraded row no discovery was performed. | $0.0030 |
| `profile-resolved` | Once per company whose candidate Bluesky URL passed the identity gate. Fires on the validation work, not on a populated count. A candidate dropped as an impersonator does not charge: the work was done and the honest answer is that there is no such profile. | $0.0020 |
| `follower-count-extracted` | Once per company where a numeric follower count was read off the public page. Does not fire on not\_extractable, blocked, identity\_mismatch or url only runs. | $0.0015 |

> 💳 **What you are billed for.** A run that resolves an account but cannot read its counts charges `company-checked` and `profile-resolved` and not `follower-count-extracted`. You are never billed for a number you did not get.
>
> Setting `includeFollowerCounts` to `false` resolves the URL only and never fires the extraction event.

**What the same coverage costs bought a la carte:** There is no Bluesky presence actor on the Apify Store priced per profile to compare against, and no Store actor sells domain to Bluesky discovery at all. The nearest comparison is what it costs to not have this: resolving a company domain to a social account by hand.

### ⌨️ Input

| Field | Type | Required | Meaning |
|---|---|---|---|
| `company_domain` | string | no | Bare company domain, for example shopify.com. Supply this or a handle. With a domain the actor runs full discovery; with a handle it skips straight to the fetch. |
| `company_name` | string | no | Optional. Improves search accuracy and is what the identity gate checks a discovered profile against, so supplying it reduces wrong matches. |
| `handle` | string | no | Optional. A Bluesky handle such as shopify.com, mamba.bsky.social, or a did. Supplying it skips discovery entirely and goes straight to the profile fetch. A company that has verified its domain with Bluesky uses the bare domain as its handle, which is why this often equals company\_domain. |
| `includeFollowerCounts` | string | no | When "true" (default) the profile page is fetched and the counts are extracted. Set "false" to resolve the profile URL only, which is cheaper and needs no proxy. Sent as a string for Clay compatibility. |
| `skipCache` | string | no | When "false" (default) a successful lookup is cached for seven days and reused. Set "true" to force a fresh fetch. Sent as a string for Clay compatibility. |
| `useActorSearch` | string | no | When "true" (default) and the domain is not itself a verified handle, Bluesky's own account search is used to find the company. Set "false" to rely only on the company homepage and the domain as handle, which avoids any chance of matching a similarly named account. Sent as a string for Clay compatibility. |

```json
{
  "company_domain": "theverge.com",
  "company_name": "The Verge",
  "useActorSearch": "true"
}
```

### 📤 Output

Exports to **JSON, CSV, Excel, HTML or XML**. One flat, snake\_case row per
company. No nested objects, so it drops straight into Clay, a spreadsheet or a
warehouse table without a flattening step.

```json
{
  "degraded": false,
  "degradation_reason": null,
  "company_domain": "theverge.com",
  "company_name": "The Verge",
  "bluesky_url": "https://bsky.app/profile/theverge.com",
  "bluesky_handle": "theverge.com",
  "bluesky_did": "did:plc:7exlcsle4mjfhu3wnhcgizz6",
  "bluesky_domain_verified": true,
  "bluesky_followers": 357220,
  "bluesky_following": 141,
  "bluesky_posts": 14584,
  "bluesky_followers_exact": true,
  "bluesky_display_name": "The Verge",
  "bluesky_bio": "Covering life in the future\n\nSubscribe here: https://www.theverge.com/subscribe",
  "bluesky_created_at": "2023-05-23T19:11:25.009Z",
  "bluesky_discovery": "domain_handle",
  "bluesky_status": "ok",
  "run_date": "2026-08-22T19:26:58.474Z"
}
```

#### false versus null, and why the difference matters

`false` means we looked and the answer is no. `null` means we could not look,
or the platform withheld it. They are never interchangeable in this output. If
you filter for companies with no presence on this platform, filter on `false`,
because `null` rows are unknown rather than absent and including them will
overstate your list.

### 💡 Tips

- **Try the domain as the handle first.** Many companies verify their domain with Bluesky, and when they have, discovery costs nothing.
- **Store the DID, not the handle,** if you are going to look this account up again later.
- **`bluesky_posts: 0` is a real and useful answer.** It means the company registered the account and never used it, which is a different qualification signal from having no account at all.
- **Filter on `bluesky_discovery`** if a wrong match would be expensive for you.

### ⚠️ Known limits

- **Bluesky is small.** It is a real network with real brand accounts and it is nowhere near the size of Instagram or LinkedIn. Many B2B companies have no Bluesky presence, and `bluesky_status: "not_found"` will be a common and correct answer. That is information, not a failure.
- **Follower counts are a public vanity metric.** They are a proxy for reach, not a measurement of it, on every platform.
- **No post content, no follower lists, no engagement data.** This actor returns account level presence and counts. It does not read posts.
- **The account search can only find accounts that exist.** A company with no Bluesky account returns `not_found` no matter how many routes are tried.

### ❓ FAQ

**Does this need a Bluesky account or an API key?**
No. The AT Protocol AppView is public and unauthenticated by design.

**Why are the counts exact here when other social actors round?**
Because Bluesky serves the integer. Instagram, TikTok, YouTube, Threads and Facebook render a rounded display string and there is no exact figure on the page to read.

**What does `bluesky_domain_verified` actually prove?**
That the account holder controls DNS for that domain, because Bluesky checks before granting the handle. It is the strongest identity evidence available in this family of actors.

**Can I pass a handle instead of a domain?**
Yes, in `handle`. It skips discovery, which is faster and cheaper.

**What happens on a company with no Bluesky account?**
You get a row with `bluesky_status: "not_found"`, the identity columns populated and the counts null. You are charged `company-checked` for the work of looking, and not the resolution or extraction events.

### 🧩 Want other GTM data?

Mamba Labs builds a fleet of GTM enrichment actors that share one flat,
Clay-ready output convention, so their rows join on `company_domain` with no
cleaning step:

| | |
|---|---|
| 🕵️ [Agent Accessibility Auditor](https://apify.com/mambalabs/agent-accessibility-auditor) | 🤖 [AI Tooling Detector](https://apify.com/mambalabs/ai-tooling-detector) |
| 📡 [B2B Buying Signals Aggregator](https://apify.com/mambalabs/b2b-buying-signals-hiring-tech-stack-intent-for-clay) | 🚀 [Prospect Engine](https://apify.com/mambalabs/b2b-prospect-engine) |
| 📝 [Publishing Frequency Tracker](https://apify.com/mambalabs/blog-publishing-frequency) | ⏩ [Sequencer Lead Push](https://apify.com/mambalabs/clay-to-instantly-smartlead-push) |
| 🔄 [Company Change-Event Feed](https://apify.com/mambalabs/company-change-event-feed) | 🧭 [Company Discovery List Builder](https://apify.com/mambalabs/company-discovery-list-builder) |
| 🏢 [Company Firmographic Enricher](https://apify.com/mambalabs/company-firmographic-enricher) | 🪪 [Company Identity Resolver](https://apify.com/mambalabs/company-identity-resolver) |
| 🌐 [Company Social Presence Mapper](https://apify.com/mambalabs/company-social-presence-mapper) | 🏷️ [Contact Classifier](https://apify.com/mambalabs/contact-classifier) |
| 📬 [Domain Deliverability Checker](https://apify.com/mambalabs/domain-deliverability-checker) | 🔗 [Domain to LinkedIn URL Resolver](https://apify.com/mambalabs/domain-to-linkedin-url-resolver) |
| ✉️ [Work Email Waterfall Finder](https://apify.com/mambalabs/email-waterfall-orchestrator) | 🎪 [Event Presence Index](https://apify.com/mambalabs/event-presence-index) |
| 💰 [Funding and Press Signal Scanner](https://apify.com/mambalabs/funding-press-signal-scanner) | 🧑‍💼 [GTM Hiring Signal Scraper](https://apify.com/mambalabs/gtm-hiring-signal-scraper) |
| 📋 [Job Posting Monitor](https://apify.com/mambalabs/gtm-job-discovery) | 🧱 [Tech Stack Detector](https://apify.com/mambalabs/gtm-tech-stack-signal-scraper) |
| 🎯 [ICP Fit Scorer](https://apify.com/mambalabs/icp-account-lead-scoring-fit-scorer-0-100-for-clay) | 🔑 [Job Board Keyword Scanner](https://apify.com/mambalabs/job-board-keyword-signal-scanner) |
| ⚖️ [Legal Entity Resolver](https://apify.com/mambalabs/legal-entity-resolver) | 💼 [LinkedIn Company Page Mapper](https://apify.com/mambalabs/linkedin-company-presence-mapper) |
| 💬 [LinkedIn Post Tracker and Comment Capture](https://apify.com/mambalabs/linkedin-post-engager-capture) | 📸 [Instagram and Facebook Brand Mapper](https://apify.com/mambalabs/meta-brand-presence-mapper) |
| 📮 [Outbound Stack Detector](https://apify.com/mambalabs/outbound-infrastructure-fingerprint) | 📄 [Page Finder and Extractor](https://apify.com/mambalabs/page-finder-extractor) |
| 👤 [People Finder and Email Verifier](https://apify.com/mambalabs/people-finder) | 📌 [Pinterest Brand Presence Mapper](https://apify.com/mambalabs/pinterest-brand-presence-mapper) |
| 🏛️ [Government Contract Award Monitor](https://apify.com/mambalabs/public-award-monitor) | 📅 [Public Company Reporting Window Finder](https://apify.com/mambalabs/public-company-reporting-window-finder) |
| 👥 [Team Page People Extractor](https://apify.com/mambalabs/team-page-people-extractor) | 🎵 [TikTok Brand Presence Mapper](https://apify.com/mambalabs/tiktok-brand-presence-mapper) |
| 🏅 [Workplace Program Detector](https://apify.com/mambalabs/workplace-program-detector) | ▶️ [YouTube Channel Stats Extractor](https://apify.com/mambalabs/youtube-channel-transcript-extractor) |

> Every actor in the suite takes a domain or a company and returns one flat row,
> so they stack in the same Clay table without reshaping anything.

> 🛠️ **Need something custom built for you or your team?** Tell us what you are
> trying to find and we will build it. [Talk to Mamba Labs](https://mambabuilt.com/contact).

### 🆘 Support

Issues, field requests and bug reports: open an issue on the actor's Issues tab.
Mamba Labs reads every one.

> ℹ️ **Sourcing and legal.** Every field comes from the public AT Protocol API that Bluesky documents and serves without authentication. `bluesky_domain_verified` reflects Bluesky's own domain handle mechanism, not an assessment by us. You are responsible for how you use the output.

Built by [Mamba Labs](https://apify.com/mambalabs).

# Actor input Schema

## `company_domain` (type: `string`):

Bare company domain, for example shopify.com. Supply this or a handle. With a domain the actor runs full discovery; with a handle it skips straight to the fetch.

## `company_name` (type: `string`):

Optional. Improves search accuracy and is what the identity gate checks a discovered profile against, so supplying it reduces wrong matches.

## `handle` (type: `string`):

Optional. A Bluesky handle such as shopify.com, mamba.bsky.social, or a did. Supplying it skips discovery entirely and goes straight to the profile fetch. A company that has verified its domain with Bluesky uses the bare domain as its handle, which is why this often equals company\_domain.

## `includeFollowerCounts` (type: `string`):

When "true" (default) the profile page is fetched and the counts are extracted. Set "false" to resolve the profile URL only, which is cheaper and needs no proxy. Sent as a string for Clay compatibility.

## `skipCache` (type: `string`):

When "false" (default) a successful lookup is cached for seven days and reused. Set "true" to force a fresh fetch. Sent as a string for Clay compatibility.

## `useActorSearch` (type: `string`):

When "true" (default) and the domain is not itself a verified handle, Bluesky's own account search is used to find the company. Set "false" to rely only on the company homepage and the domain as handle, which avoids any chance of matching a similarly named account. Sent as a string for Clay compatibility.

## `source_tag` (type: `string`):

Internal attribution tag set by Mamba Labs on published task examples. Not required, and nothing depends on it. Leave it empty.

## Actor input object example

```json
{
  "company_domain": "shopify.com",
  "company_name": "Shopify",
  "includeFollowerCounts": "true",
  "skipCache": "false",
  "useActorSearch": "true"
}
```

# Actor output Schema

## `results` (type: `string`):

Dataset of one flat row per company, with per platform status so a blocked fetch never reads as a zero.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "company_domain": "shopify.com",
    "company_name": "Shopify",
    "handle": ""
};

// Run the Actor and wait for it to finish
const run = await client.actor("mambalabs/bluesky-brand-presence-mapper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "company_domain": "shopify.com",
    "company_name": "Shopify",
    "handle": "",
}

# Run the Actor and wait for it to finish
run = client.actor("mambalabs/bluesky-brand-presence-mapper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "company_domain": "shopify.com",
  "company_name": "Shopify",
  "handle": ""
}' |
apify call mambalabs/bluesky-brand-presence-mapper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,mambalabs/bluesky-brand-presence-mapper"
        }
    }
}

```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/eLpxzP4IuXznlFVND/builds/C9OXaWK03crPaWGhl/openapi.json
