# theHarvester OSINT: Hosts, Subdomains & Email (`khadinakbar/theharvester-osint`) Actor

Find public subdomains, hostnames, IP addresses, and email addresses for one authorized domain using passive theHarvester sources.

- **URL**: https://apify.com/khadinakbar/theharvester-osint.md
- **Developed by:** [Khadin Akbar](https://apify.com/khadinakbar) (community)
- **Categories:** Developer tools, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $12.00 / 1,000 normalized finding returneds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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

## theHarvester OSINT: Hosts, Subdomains & Email

Collect a source-attributed public footprint for one authorized domain: in-scope subdomains and hostnames, public email addresses returned by selected providers, and optional passive IP observations. The dataset gives an agent or analyst one normalized row shape, while `OUTPUT` and `RUN_SUMMARY` make source coverage, limits, and terminal status explicit.

### Best fit and workflow

Choose this Actor when you have a domain and want a bounded passive discovery pass before an asset inventory, exposure review, or authorized security assessment. It works well as the first step in a workflow that turns a domain into a review queue: collect findings here, verify ownership and reachability in your own process, then enrich only the assets that your authorization covers.

A practical scenario is a security team reviewing `example.com` after a rebrand. They run the default source groups, inspect hostname and email rows, use `sources` to repeat a provider-specific check, and keep `RUN_SUMMARY` beside the export so every row retains its discovery engine and collection time.

The runtime is intentionally a run-based dataset Actor rather than a Standby API. It accepts one domain per run, expands friendly capability tokens into an explicit passive allowlist, and uses the pinned upstream theHarvester JSONL result model. The upstream project describes passive P0 sources and the authorized-use boundary in its [README](https://github.com/laramies/theHarvester/blob/master/README.md).

### Quick start

Run the default hostname, email, and passive-IP workflow:

```json
{
  "domain": "example.com",
  "sources": ["subdomains", "emails"],
  "maxResults": 50,
  "perSourceLimit": 25,
  "sourceWorkers": 6,
  "includeIps": true,
  "startOffset": 0
}
```

From a terminal, call the private or public Actor by its canonical owner/slug:

```bash
curl -X POST "https://api.apify.com/v2/acts/khadinakbar~theharvester-osint/runs?token=$APIFY_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"domain":"example.com","sources":["subdomains","emails"],"maxResults":20}'
```

Read the default dataset after the run:

```bash
curl "https://api.apify.com/v2/datasets/$DATASET_ID/items?clean=true&format=json"
```

### Input reference

| Field | Default | Accepted range or values | Use |
| --- | --- | --- | --- |
| `domain` | required | One bare domain or HTTP(S) URL | Target scope, normalized to lowercase ASCII. |
| `sources` | `subdomains`, `emails` | Friendly groups or named passive providers | Expands into explicit P0 theHarvester sources. |
| `maxResults` | `50` | `1`–`250` | Total normalized dataset rows and maximum finding charges. |
| `perSourceLimit` | `25` | `1`–`200` | Provider-result bound for each selected source. |
| `sourceWorkers` | `6` | `1`–`8` | Concurrent passive provider jobs. |
| `includeIps` | `true` | Boolean | Includes IP rows when a selected passive source reports them. |
| `startOffset` | `0` | `0`–`500` | Pagination hint for providers that support offsets. |

The source groups resolve as follows:

- `subdomains`: the curated passive hostname set.
- `emails`: the curated email-capable set.
- `all-passive`: the full curated P0 set.
- A named provider: one exact provider from the input schema allowlist.

This Actor does not accept API keys, cookies, passwords, proxy credentials, wildcard domains, IP targets, DNS brute force, recursive DNS, virtual-host probing, takeover checks, screenshots, or direct host interaction. Public provider availability varies by domain and time; source-level statuses remain in `RUN_SUMMARY`.

### Output data

The default dataset contains one row per canonical finding. `recordType` is `hostname`, `email`, or `ip`; `value` is the universal lookup value; and the type-specific field makes filtering straightforward. Hostname rows are restricted to names below the target domain and carry `isSubdomain: true`.

| Field | Meaning |
| --- | --- |
| `recordType` | Stable discriminator: `hostname`, `email`, or `ip`. |
| `domain` | Normalized target domain. |
| `value` | Canonical hostname, email, or IP address. |
| `hostname`, `email`, `ipAddress` | Type-specific convenience fields; other fields are `null`. |
| `isSubdomain` | `true` for hostname rows and `null` for email/IP rows. |
| `sources`, `sourceCount` | Providers that attributed the value. |
| `provenance` | Pinned engine/version, currently `theHarvester 5.0.0`. |
| `discoveredAt` | UTC normalization time. |

Illustrative dataset row:

```json
{
  "recordType": "hostname",
  "domain": "example.com",
  "value": "api.example.com",
  "hostname": "api.example.com",
  "email": null,
  "ipAddress": null,
  "isSubdomain": true,
  "sources": ["crtsh", "urlscan"],
  "sourceCount": 2,
  "provenance": "theHarvester 5.0.0",
  "discoveredAt": "2026-08-30T12:00:00Z"
}
```

`OUTPUT` contains the machine-readable terminal outcome, pushed item count, type counts, charge counts, and warnings. `RUN_SUMMARY` adds the expanded source list, upstream status, raw and filtered counts, duplicate count, per-source execution summaries, deadline state, and safe write diagnostics. The outcome values are `COMPLETE`, `PARTIAL`, `VALID_EMPTY`, `INVALID_INPUT`, `UPSTREAM_FAILED`, and `CONFIG_ERROR`.

### Pricing

This Actor uses Apify Pay per event (PPE): `$0.012` for each validated finding row persisted to the dataset, plus the platform usage charge shown by Apify. Apify also applies the configured synthetic Actor-start event of `$0.00005`. For example, a run capped at 20 persisted findings has a maximum finding-event charge of `$0.24`, plus the start event and platform usage; a source returning fewer rows costs less. Check the live Actor Pricing tab for the current configured price before running a paid job.

`maxResults` is the visible cost and output bound. A charge is attempted only after a row passes the Pydantic output contract and includes provider provenance. When Apify reports an event limit, the Actor stops adding rows and records `PARTIAL` with the charge-limit warning.

### Best results and recovery

Start with the default capability groups for a broad passive view, then repeat with a smaller named source set when you need provider-level comparison. Keep `perSourceLimit` modest for a quick inventory and raise it when a domain has a larger public footprint. Use `includeIps: false` for a hostname/email-only export.

Provider results are observations rather than ownership proof. Combine the dataset with your authorized asset inventory, independently verify important findings, and retain `RUN_SUMMARY` with each export. A run with a completed source and zero accepted rows is reported as `VALID_EMPTY`; partial provider coverage, deadline coverage, or persistence coverage is reported as `PARTIAL`, so the next action is visible without treating an empty table as proof of absence.

### MCP and AI-agent use

Connect the Actor through the Apify MCP configurator at `mcp.apify.com` and ask an agent for a bounded dataset job:

> For the authorized domain `example.com`, collect passive subdomains and public emails with at most 50 findings, include IP observations, and return the dataset URL plus the `OUTPUT` and `RUN_SUMMARY` records. Summarize each finding’s source providers and collection time, and label partial or empty source coverage clearly.

The useful agent handoff is the dataset plus readback: filter `recordType` for hostnames or emails, group by `sources`, page through the dataset API, and inspect `RUN_SUMMARY` before using a finding in a downstream assessment. The Actor remains read-only with respect to target systems and does not receive provider credentials.

### Focused standalone workflow

This Actor is designed as a focused standalone workflow for passive domain discovery. It produces a complete bounded handoff for the next authorized inventory or verification step, so a separate Actor link is unnecessary until a specific downstream enrichment job is selected.

### Builder's note

I built this wrapper around the upstream transport-neutral execution API and its normalized JSONL result stream so the Apify dataset does not depend on scraping terminal text. I keep source selection explicit because the upstream project supports broader capability selectors, while this product contract is passive and bounded. The result model preserves provider attribution, and the Actor adds a second validation layer before any paid dataset write.

### Responsible use

Run this Actor only for a domain you own or are authorized to assess. Respect applicable laws, provider policies, and site terms; treat public findings as leads for verification rather than proof of ownership, control, or vulnerability. The Actor is a passive discovery aid, not a substitute for authorization, asset inventory, or a security assessment.

### Freshness and release evidence

The engine is pinned to reviewed upstream commit `903a35e118ccafccd750e9e1842055f01dc8b709`; provider behavior, result availability, and pricing remain live external state. The deployed release log and cloud canary evidence are the source of truth for readiness. A local build or a successful process exit alone does not establish provider coverage or release readiness.

# Actor input Schema

## `domain` (type: `string`):

Enter one domain you own or are authorized to assess, such as example.com. An http(s) URL is also accepted and reduced to its hostname. IP addresses, wildcards, ports, credentials, and multiple targets are outside this input contract.

## `sources` (type: `array`):

Choose subdomains for the curated passive hostname set, emails for the curated email set, all-passive for both plus passive IP-capable providers, or named providers from the allowlist. P1 DNS interaction, direct requests, takeover checks, screenshots, and credential-bearing providers are not exposed.

## `maxResults` (type: `integer`):

Maximum normalized rows written to the default dataset across hostname, email, and IP records. Each persisted row is one finding-returned charge. The default is 50 and the hard input maximum is 250.

## `perSourceLimit` (type: `integer`):

Bound the number of results requested from each selected provider. This is a provider-result limit, not the total dataset size. The default is 25 and the maximum is 200.

## `sourceWorkers` (type: `integer`):

Maximum selected providers queried concurrently. The default of 6 balances collection time and upstream rate limits; lower it when a source set is sensitive to concurrency. This is not a browser or DNS concurrency setting.

## `includeIps` (type: `boolean`):

Return IP addresses when a selected passive source reports them. Defaults to true. Set false when the dataset should contain only hostnames/subdomains and emails; the run summary still records that IP findings were observed.

## `startOffset` (type: `integer`):

Starting offset passed to providers that support pagination. Use 0 for the first page. This is an upstream pagination hint and does not guarantee every provider implements offsets identically.

## Actor input object example

```json
{
  "domain": "example.com",
  "sources": [
    "subdomains",
    "emails"
  ],
  "maxResults": 20,
  "perSourceLimit": 10,
  "sourceWorkers": 4,
  "includeIps": true,
  "startOffset": 0
}
```

# Actor output Schema

## `findings` (type: `string`):

Default dataset: one source-attributed row per hostname, email, or selected IP finding.

## `findingsCsv` (type: `string`):

Default dataset exported as CSV.

## `output` (type: `string`):

Terminal outcome, target domain, item counts, charge counts, and warnings.

## `runSummary` (type: `string`):

Source selection, upstream evidence status, normalization, persistence, deadline, and safe diagnostics.

## `failures` (type: `string`):

Bounded source statuses and error types when providers return partial evidence.

## `consoleRun` (type: `string`):

Inspect logs, the findings dataset, and terminal key-value records.

# 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 = {
    "domain": "example.com",
    "sources": [
        "subdomains",
        "emails"
    ],
    "maxResults": 20,
    "perSourceLimit": 10,
    "sourceWorkers": 4,
    "includeIps": true,
    "startOffset": 0
};

// Run the Actor and wait for it to finish
const run = await client.actor("khadinakbar/theharvester-osint").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 = {
    "domain": "example.com",
    "sources": [
        "subdomains",
        "emails",
    ],
    "maxResults": 20,
    "perSourceLimit": 10,
    "sourceWorkers": 4,
    "includeIps": True,
    "startOffset": 0,
}

# Run the Actor and wait for it to finish
run = client.actor("khadinakbar/theharvester-osint").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 '{
  "domain": "example.com",
  "sources": [
    "subdomains",
    "emails"
  ],
  "maxResults": 20,
  "perSourceLimit": 10,
  "sourceWorkers": 4,
  "includeIps": true,
  "startOffset": 0
}' |
apify call khadinakbar/theharvester-osint --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,khadinakbar/theharvester-osint"
        }
    }
}

```

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/PZTcnwcWSmpe7ffBO/builds/6wHq8ViyEBCSxQ1YC/openapi.json
