# QQ and 163 Chinese Email Address Validator (`zinin/qq-163-chinese-email-address-validator`) Actor

Validate supplied Chinese email addresses with provider shape hints and optional DNS MX checks. Export honest status with mailbox deliverability unknown.

- **URL**: https://apify.com/zinin/qq-163-chinese-email-address-validator.md
- **Developed by:** [Tim Zinin](https://apify.com/zinin) (community)
- **Categories:**
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.70 / 1,000 validated addresses

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

## QQ and 163 Chinese Email Address Validator

Validate supplied QQ, 163 and other Chinese email addresses with address syntax, provider shape hints and optional DNS MX evidence. This Actor checks your existing list without SMTP probes or sending email, and always reports mailbox deliverability as unknown.

**Evidence status: private build 0.1.2 accepted on 2026-09-06.** Every input/output example below is an exact Dataset record or OUTPUT record from the accepted acceptance runs on Apify's network with the pay-per-event tariff active. The listing is not public yet; no scheduled task or review history is claimed.

Planned pay-per-event price: **$0.005 per run start + $0.001 per checked address**. Incomplete and error rows are free of result charges.

### What you get

- ASCII dot-atom address syntax with IDN domain normalization.
- QQ and NetEase address-shape hints, without claiming an account is registered.
- Optional MX checks with five-second timeouts, three workers and a 100-domain cap.
- One DNS query per unique domain; complete invalid-syntax checks still produce useful results.
- Timeouts and resolver errors remain free partial rows, with no PII in Actor log messages.
- Runs on Apify: schedule it, monitor it, call it from your API workflow, and export results to JSON, CSV or Excel.

![Input, evidence and result boundary](assets/readme-hero.svg)

### How to run it

1. Open the Actor input form and start with the clearly marked synthetic prefill.
2. Inspect the result dataset and OUTPUT before replacing the demo with authorized buyer data.
3. Supply either your inline records or your dataset ID, then choose the documented settings and limits.
4. Start a small run, inspect partial/error records, and export the relevant complete result types.

### Pricing

The accepted Wave B build plan specifies **$0.005 per run start + $0.001 per checked address**. At that planned base price, 100 complete units in one run are $0.105, and 1,000 are $1.005. This arithmetic excludes any separate upstream export purchase or downstream service cost. There is no monthly seat charge in this planned Actor tariff.

The actual deployed tariff has not been changed or verified by this build. The orchestrator must reconcile the Store price, event definitions and acceptance receipts before making this a final listing. The platform's current free-plan credit availability is separate from this Actor's result-event semantics; do not treat free result rows as a promise of a zero-cost run.

Billing unit: **checked address**. Bill result-found once per complete supplied-address check, including definite syntax-invalid addresses, null MX, no MX and NXDOMAIN. Timeout, resolver errors, malformed input and domain-limit partials are free. Deliverability must remain unknown for every address. Offline prefill checkMx=false charges three checked addresses; DNS goldens require recorded cloud DNS evidence, not a claim that the mailbox exists.

### Input contract

The table describes the actual input schema. Structural restrictions that Apify's form dialect cannot express are checked again at runtime. Defaults and prefill are distinct: the email form's prefill sets checkMx=false for the offline demo even though non-demo validation normally enables MX.

| Field | Type and bound | Meaning |
|---|---|---|
| `emails` | array; max 1000 items | Replace the clearly marked synthetic prefill with your own authorized data. Explicit datasetId suppresses unchanged demo rows. Runtime: 10 MiB input, 64 KiB per row, depth 16 and 100 items per nested array. |
| `datasetId` | string; max 128 chars | Optional Apify dataset ID in your account. Read in pages of 100, at most 5000 rows and 10 MiB. No dataset URL. Remove custom inline rows when setting this. |
| `field` | string; max 80 chars | Dotted field path to the address in dataset records; inline emails are strings. |
| `checkMx` | boolean | Use DNS MX only, timeout 5 seconds, concurrency 3 and at most 100 unique domains. Prefill disables DNS for offline demonstration. |
| `maxItems` | integer; 1–5000 | Working output cap; remaining units are reported as unprocessed in OUTPUT. Error notices and free supplier summaries are additional. |

#### Recorded happy-path input and output

Run `PCx49YYbYHIBWD0SW` on build 0.1.2, 2026-09-06, 2 s, Dataset records: 3, charged events: `{"apify-actor-start": 1, "result-found": 3}`. The input is the form prefill; every one of the 3 paid records carries the demo marker because the prefill is synthetic.

Input:

```json
{
 "field": "email",
 "checkMx": true,
 "maxItems": 1000
}
```

First Dataset record (exact):

```json
{
 "schemaVersion": "1.0",
 "type": "email_validation",
 "sourceUrl": null,
 "found": true,
 "status": "ok",
 "resultCount": 1,
 "partial": false,
 "error": "",
 "warnings": [
  "Provider rules are shape hints, not account registration checks."
 ],
 "checkedAt": "2026-09-06T02:09:51.366Z",
 "evidence": "buyer_address_and_dns_mx",
 "confidence": null,
 "action": "review",
 "demo": true,
 "inputIndex": 0,
 "email": "demo@qq.com",
 "normalized": "demo@qq.com",
 "domain": "qq.com",
 "provider": "QQ",
 "syntaxValid": true,
 "providerRule": "qq_alias_shape",
 "mxStatus": "present",
 "mxHosts": [
  {
   "priority": 10,
   "exchange": "mx3.qq.com"
  },
  {
   "priority": 20,
   "exchange": "mx2.qq.com"
  },
  {
   "priority": 30,
   "exchange": "mx1.qq.com"
  }
 ],
 "disposableFlag": false,
 "deliverability": "unknown",
 "synthetic": true
}
```

#### Recorded partial input and output

Run `LVBAF9IzP9WmRQdKv` on build 0.1.2, 2026-09-06, 2 s, Dataset records: 2, charged events: `{"apify-actor-start": 1, "result-found": 1}`. The free notice keeps the remaining work count; the complete neighbouring result still carries its own result event.

Input:

```json
{
 "maxItems": 1,
 "field": "email",
 "checkMx": true
}
```

Free notice record (exact):

```json
{
 "schemaVersion": "1.0",
 "type": "notice",
 "sourceUrl": null,
 "found": false,
 "status": "partial",
 "resultCount": 0,
 "partial": true,
 "error": "max_items",
 "warnings": [],
 "checkedAt": "2026-09-06T02:09:56.660Z",
 "evidence": "buyer_supplied_data",
 "confidence": null,
 "action": "review",
 "demo": true,
 "unprocessed": 2
}
```

#### supplied addresses — live DNS MX observation on qq.com and 163.com

Run `SugZe8FMoPDHLlmQE` on build 0.1.2, 2026-09-06, 2 s, Dataset records: 3, charged events: `{"apify-actor-start": 1, "result-found": 3}`. 3 paid records. Inline input; the Actor performed DNS MX lookups only, no SMTP or HTTP.

Input:

```json
{
 "emails": [
  "demo@qq.com",
  "demo@163.com",
  "bad-address"
 ],
 "checkMx": true,
 "field": "email",
 "maxItems": 1000
}
```

First paid Dataset record (exact):

```json
{
 "schemaVersion": "1.0",
 "type": "email_validation",
 "sourceUrl": null,
 "found": true,
 "status": "ok",
 "resultCount": 1,
 "partial": false,
 "error": "",
 "warnings": [
  "Provider rules are shape hints, not account registration checks."
 ],
 "checkedAt": "2026-09-06T02:10:00.615Z",
 "evidence": "buyer_address_and_dns_mx",
 "confidence": null,
 "action": "review",
 "demo": true,
 "inputIndex": 0,
 "email": "demo@qq.com",
 "normalized": "demo@qq.com",
 "domain": "qq.com",
 "provider": "QQ",
 "syntaxValid": true,
 "providerRule": "qq_alias_shape",
 "mxStatus": "present",
 "mxHosts": [
  {
   "priority": 10,
   "exchange": "mx3.qq.com"
  },
  {
   "priority": 20,
   "exchange": "mx2.qq.com"
  },
  {
   "priority": 30,
   "exchange": "mx1.qq.com"
  }
 ],
 "disposableFlag": false,
 "deliverability": "unknown",
 "synthetic": true
}
```

### Output and field dictionary

The overview table exposes every field used by the complete, summary and notice records produced by this Actor. A field may be absent on unrelated record types. Keep the type discriminator when flattening a mixed dataset.

#### `schemaVersion`

Version of the output contract. Compare it before appending rows to a table created for an earlier schema.

#### `type`

Discriminator for this dataset record. Filter by it before aggregating values, because result rows and free operational notices share one dataset.

#### `sourceUrl`

An inert source reference when the record has one. It is never an instruction for this Actor to fetch a social, product or contact page.

#### `found`

True means a complete billable result under this Actor's selected unit. False includes useful free previews and summaries as well as absent, rejected or partial results.

#### `status`

Compact processing state. Use it together with found, partial and error; a single status label is not a substitute for the full result contract.

#### `resultCount`

One for a complete billable result, zero for free records. This is a per-record count, not the total number of rows processed in the run.

#### `partial`

True means this row or notice represents incomplete work. Never promote a partial row to a complete result just because other fields are populated.

#### `error`

Empty for correct no-data and complete results; a bounded classification for a failure. Provider error bodies and input secrets are not copied here.

#### `warnings`

Readable qualifications that can accompany a complete result. A warning is not automatically an error or a reason to drop the row.

#### `checkedAt`

ISO observation time for this Actor's output. It does not replace the supplied post timestamp, the exchange-rate date or an upstream export collection time.

#### `evidence`

Origin label for the result's evidence. Supplied data, local syntax checks and DNS observations establish different boundaries.

#### `confidence`

Null when the Actor has no measured confidence model. Null is deliberate and should not be filled with an arbitrary 100 percent score.

#### `action`

A conservative review hint. It is not an executed downstream action or authorization to contact, purchase, publish or delete anything.

#### `demo`

True on synthetic demonstration output. Keep this field visible when combining demo runs with real buyer data in an internal workbook.

#### `unprocessed`

Remaining units reported by a free notice. Consult OUTPUT for the run-level remainder and the reason the working limit stopped processing.

#### `inputIndex`

Zero-based position in the supplied inline list or loaded dataset. Keep the original export if you need to resolve this index later.

#### `email`

Trimmed supplied address in the buyer's result dataset. This field is personal data and is excluded from log and OUTPUT summaries.

#### `normalized`

Supported local part plus normalized lowercase/IDN domain. Null for invalid or unsupported syntax; no guessed correction is made.

#### `domain`

Normalized domain used for optional MX deduplication. It is null when the address does not pass the supported syntax.

#### `provider`

Fixed domain-map label such as QQ or 163, otherwise other. It is not inferred from a server banner or a remote account lookup.

#### `syntaxValid`

Whether the address fits the selected ASCII dot-atom subset. Read warnings before treating unsupported EAI or quoted syntax as universally invalid.

#### `providerRule`

Provider-specific shape hint. A recognized alias pattern does not prove registration, activation or ownership of the mailbox.

#### `mxStatus`

DNS or workflow classification: present, null\_mx, no\_mx, nxdomain, timeout, dns\_error, domain\_limit, not\_checked or skipped\_invalid\_syntax.

#### `mxHosts`

Sorted MX exchange/priority evidence. Hosts are reported as data and are never contacted by this Actor.

#### `disposableFlag`

Exact membership in a small local hint list. False does not prove that a domain is permanent or reputable.

#### `deliverability`

Always unknown. Syntax and DNS observations do not establish the existence or acceptance behavior of a particular mailbox.

#### `synthetic`

Additional provenance marker for synthetic records. The explicit demo field remains the common filter across all four B2 Actors.

### Who uses it

CRM operators, migration teams and form-quality analysts use syntax and MX observations to improve supplied lists while keeping mailbox existence and consent separate.

#### What an address check establishes

An address has several independent properties: its supplied string can match a supported syntax, its domain can advertise a mail exchanger, and a particular mailbox can exist or accept mail. This Actor checks the first two properties only. `deliverability` is always `unknown`, including when syntax is valid and MX records are present. No SMTP session, recipient probe, HTTP verification service or test email is used to turn those limited observations into a stronger claim.

The default input contains `demo@qq.com`, `demo@163.com`, and `bad-address`. These are synthetic demonstration values, not assertions that the named mailboxes exist. The default preview input disables MX so it runs without network access. All three rows are completed checks, including the definite syntax-invalid value, so all three can produce a result event under the planned checked-address tariff. The invalid value demonstrates a useful rejection outcome rather than an infrastructure error.

#### Syntax and normalization boundaries

The supported local-part syntax is ASCII dot-atom form. Letters, digits and standard dot-atom symbols are accepted, with dots separating nonempty segments. Consecutive dots, leading or trailing dots, internal whitespace and multiple at-signs do not pass. The local part is limited to 64 characters, and supplied address strings are limited to 254 characters. Leading and trailing whitespace is trimmed before the address is evaluated.

Quoted local parts and SMTPUTF8 local parts are outside this version's supported subset. They return a syntax warning and a false syntax result, which should be routed as unsupported or invalid for this workflow rather than a universal statement about every possible mail system. An address such as a Chinese-character local part may be meaningful to an EAI-capable system even though it does not pass this Actor's selected subset. The warning is part of the output contract.

The domain is converted with Node's IDN conversion and lowercased. Domain labels must be bounded, nonempty and structurally valid; a domain must contain a dot. The local part retains its supplied case after trimming. The Actor does not assume that every provider ignores case or that all aliases can be merged safely. An invalid or unsupported address has `normalized=null`, because a guessed correction could point at a different person.

#### Provider rules are shape hints

QQ addresses are classified as a numeric shape, an alias shape or an unconfirmed alias shape. A numeric shape follows the bounded nonzero-leading digit pattern used in this version's rule set. An alias shape begins with a letter and uses the permitted common alias characters. These categories do not query QQ, prove that a QQ number is allocated, or verify that a particular account has activated email service.

For 163, 126 and Yeah domains, the Actor distinguishes a conventional letter-led alias, an eleven-digit mobile-shaped alias and an unconfirmed alias shape. Other listed providers, including Foxmail, Aliyun and Sina, receive a provider-alias hint without registration verification. A syntactically valid address can retain an unconfirmed provider shape and still be a complete address check. Provider rules are descriptive evidence, not a reason to manufacture a mailbox-existence verdict.

The provider label comes from a fixed domain map. Unknown domains receive `other`, not an inferred corporate provider from a logo or DNS hostname. A small fixed disposable-domain hint list marks exact known entries. `disposableFlag=false` means the domain was not in that small list; it is not proof that the domain is permanent, trustworthy or suitable for a customer relationship. The list is versioned in the Actor source rather than retrieved from a remote service.

#### MX outcomes and their interpretation

`present` means the resolver returned structurally valid MX records. Hosts are sorted by priority and normalized for display. They are not opened and their addresses are not used for outbound SMTP. The output preserves priority because it is useful DNS evidence, but the Actor does not select a mail server and attempt delivery.

`null_mx` means a sole priority-zero MX record points at the DNS root/empty exchange, representing an explicit null MX response. `no_mx` means no MX records were returned or the resolver reported no MX data. `nxdomain` means the resolver reported that the domain was not found. These are complete observations and are billable address checks. They do not change `deliverability` from unknown, because this Actor's output intentionally separates DNS evidence from a mailbox claim.

`timeout`, `dns_error`, and `domain_limit` are incomplete MX checks. They produce free partial rows. A temporary resolver failure does not prove that the domain has no mail service. A malformed record set, mixed null MX and ordinary MX records, invalid priorities or invalid exchanger hostnames is treated as a resolver-data error rather than a clean result. A consumer should keep those rows in a retry or review queue instead of deleting the addresses as nonexistent.

`not_checked` means MX was intentionally disabled for a syntactically valid address. `skipped_invalid_syntax` means DNS was unnecessary because the supplied address did not pass the supported syntax. Those statuses are complete for the selected workflow. The buyer can run an offline syntax pass before enabling MX, which avoids unnecessary DNS work on strings that already need correction or a different address-syntax policy.

#### Domain deduplication, concurrency and limits

The Actor deduplicates domain lookups, not address result rows. Five addresses at qq.com can share one MX query while still producing five checked-address results. Each supplied row remains traceable through `inputIndex`. Repeated addresses are not silently removed because the plan's billable unit is the checked supplied address and the deduplication contract applies specifically to domains.

At most three DNS lookups run concurrently. Each lookup uses promise-based `resolveMx` on a dedicated `node:dns/promises.Resolver` with a five-second timeout. Its resolver is cancelled when the lookup finishes or times out, so timed-out native requests do not accumulate behind the three-worker limit. The Actor checks at most 100 unique domains per run. Additional syntactically valid domains receive free `domain_limit` partial results. These caps are fixed runtime constraints rather than buyer-controlled high-concurrency settings. A workload of many addresses at a few domains is therefore materially different from a workload with one address at thousands of domains.

The inline email list is limited to 1,000 values. A buyer dataset can provide up to 5,000 records and 10 MiB, with the selected dotted `field` locating the email in each record. The working `maxItems` cap applies before preparing DNS work. A budget that cannot cover further completed checks limits the selected prefix; remaining input rows are reported in OUTPUT. A free DNS failure does not become a paid result simply to fill a preallocated count.

#### Personal-data handling and downstream actions

The dataset intentionally contains the supplied address and its normalized form because these are the buyer's requested useful output. Actor log messages and the OUTPUT summary do not contain address strings. Resolver errors are converted to bounded classifications rather than copied with their original messages. The Actor does not enrich an address with a person's name, profile, phone number or social account.

For a CRM cleanup workflow, route supported syntax failures to a correction or manual-review queue. Keep domain-level infrastructure observations in separate fields from an existing customer-consent record. A successful syntax/MX check does not create permission to contact the address, and a disposable-domain hint is not a substitute for a consent or identity process. Restrict the source dataset and exported files to the people and systems that need them.

For a form-quality workflow, aggregate counts of syntax failures and unconfirmed shapes without exporting a new address list into broad reporting channels. For a migration, compare source row counts with OUTPUT, retain a stable original record ID in your own join table, and investigate partial rows separately. This Actor reads the email field you select; it does not write back to your CRM or modify the original dataset.

#### Retry and retention choices

A timeout can be retried in a later authorized run after the resolver or network recovers. The Actor does not keep a global long-lived DNS cache across runs, so a later run can observe changed MX evidence. It also does not promise exactly-once charging across manual replays: replaying a completed address check creates a new check. Store the run ID and observation timestamp if you need an audit trail of why a particular row was routed for review.

Retain raw addresses only as long as your own purpose and obligations require. The Actor itself produces no new message or external contact, but exporting a dataset can broaden access to personal data. A team sharing results should normally share aggregate counts and the specific partial/error reason with operators who need the raw values, rather than copying all addresses into general chat notifications.

### Evidence and boundaries

The observations on this page come from accepted platform runs (see Sources and release evidence); local SDK 3.7.2 tests replace external services with deterministic fixtures and cover the paths a platform run cannot show.

The Actor reads data supplied by the buyer. Choosing a dataset ID uses the buyer's permitted Apify storage access; it does not authorize access to someone else's private dataset. A dataset ID is an identifier, not a fetch URL. The original source dataset is read only. The Actor writes its own default result dataset and one OUTPUT summary. It does not overwrite upstream rows or silently publish exports somewhere else.

Synthetic input and output carry explicit demo provenance. Keep `demo` visible when reviewing a run and inspect `demoInput` in OUTPUT. A dataset ID suppresses the unchanged form prefill. If a custom inline list is also supplied, the Actor rejects the ambiguous combination instead of merging buyer records with demonstration records. Empty input is meaningful and produces a correct free no-data record; it does not reactivate the default demo.

#### Rights and supplied personal data

Use exports and addresses that you are entitled to process for your stated purpose. A purchased export or an accessible API response does not by itself grant unlimited redistribution rights over every text, identifier or personal detail it contains. The buyer is responsible for applicable source terms, permissions, retention and privacy obligations, including relevant PIPL and GDPR responsibilities. This Actor does not determine the legal basis of an individual dataset or supply consent for a later communication.

Source URLs and references remain data. This batch does not request Douyin, Xiaohongshu, Weibo, Zhihu, consumer WeChat, Bilibili, Baidu, Taobao, 1688, JD, Toutiao, Juejin or 36kr pages. It does not log into those platforms, use account cookies, open attachments, execute supplied code or follow embedded links. The specific corporate webhook exception is limited to the sender's reviewed destination endpoints and requires buyer-supplied configuration.

### Decision routing

Start with OUTPUT, then inspect the relevant dataset record types. A nonempty dataset is not enough to establish success: it may contain free rejected-input records, previews or a partial notice. A clean process exit is not a delivery receipt from an external platform. Likewise, a free result row can be useful and intentional rather than a defect. The correct interpretation depends on the Actor's billable unit and selected mode.

Route `partial=true` records to a separate review or retry path. Keep valid complete neighboring results; do not discard an entire run simply because one supplied record was malformed. For a correct empty input, `found=false`, `error=""`, and `resultCount=0` describe absence without failure. For infrastructure errors, the error classification remains nonempty. For fatal billing or persistence uncertainty, stop automated continuation until the run is reconciled.

`replaySafe=false` is deliberate. Repeating a run can create new result records and new result events. The Actor does not maintain a cross-run exactly-once ledger, transactional outbox or reservation service. A repeated webhook request can have an external effect; a repeated analytics or normalization run can create duplicate rows in your downstream warehouse if your own import does not use a run identifier. Plan your joins and retries accordingly.

#### Before an unattended schedule

Run the smallest useful input and inspect both the result dataset and OUTPUT. Verify that the expected field paths are populated, that null values are understood and that the selected cap is appropriate. Save a task only after its settings represent the intended recurring workload. A schedule automates those saved settings; it does not repair an exporter schema change, grant source permissions or reconcile uncertain delivery for you.

Keep one operational owner for the source dataset and one for the consumer of the result. A source owner can explain changed column names or missing counters; a result owner can decide whether a partial observation should delay a report or notification. The Actor returns concrete evidence to support that decision instead of inventing missing data to keep a pipeline superficially green.

### Integration recipes

#### Console to a workbook

Begin with the synthetic prefill so you can inspect the shape quickly. Replace it with a small authorized sample and rerun. Use the Dataset table's `overview` view to see all result and summary fields, then filter by `type` before exporting. A CSV is convenient for scalar columns; nested arrays and objects are better preserved in JSON. If your spreadsheet flattens nested values, retain the original JSON export for later investigation.

Add the run ID and selected settings to your workbook as separate metadata. A row's `checkedAt` says when this Actor produced it, but cannot replace every upstream timestamp or source observation date. Do not append two runs blindly and assume the combined row count is a unique count of products, people, posts or deliveries. Use the Actor-specific identity and deduplication rules when constructing a downstream key.

#### An upstream Actor and a dataset ID

Let your authorized upstream process finish and obtain its dataset ID. Supply that ID in `datasetId`, remove any custom inline records and choose the exact field map or email field your workflow needs. The Actor requests at most 100 records per page, caps input at 5,000 records and rejects more than 10 MiB of UTF-8 record data. It does not accept a public dataset URL in place of an ID.

Preserve the upstream run reference in your own orchestration record. If the upstream exporter changes its output shape, compare a raw record with the automatic mapping candidates or selected dotted paths. A mapping error should be corrected explicitly; repeating the same run with a higher maxItems value cannot create a missing source field. Local fixture tests cover known shapes, while actual cloud acceptance must verify the purchased export in the buyer account.

#### API orchestration without a second write channel

Create and run the Actor through your normal Apify workflow, then read its default dataset and OUTPUT record. This package does not contain a separate export-to-CRM credential or an implicit message destination. A consumer that writes to a warehouse, CRM or chat service should make that action explicit in its own configuration and retain the source run ID. This keeps a result computation reviewable before it triggers another business process.

Check the platform run status, OUTPUT fatal field and partial counts before ingesting complete rows. If a request to start a run times out in your orchestration layer, inspect whether a run was created before starting another one. A client-side timeout does not establish that no run exists. This general reconciliation habit is particularly important when your downstream action is not easily reversible.

#### JSON and CSV handling

Use JSON when you need exact nested evidence, such as mapping paths, tiers, MX hosts or rendered payloads. Use CSV after selecting a single record type and a deliberate set of scalar columns. An array rendered as a JSON string inside a CSV cell is still one cell, not a guarantee that a spreadsheet can calculate on its contents. Document the flattening rule in your import job rather than silently dropping nested values.

Null, zero, false and the empty string have distinct meanings. Null means unavailable or inapplicable; zero is a known numerical value; false is an explicit boolean state; an empty error string is a correct absence of error. A data-cleaning step that replaces all four with blanks can destroy the boundaries this Actor is designed to preserve. Keep these values distinct in a database or typed dataframe.

### Operating guide

#### Input size and structure

The runtime checks both array lengths and byte sizes. One source record is limited to 64 KiB. Opaque nested arrays have a maximum of 100 elements, nesting depth is capped at sixteen (real Xiaohongshu exports nest to nine) and source objects have a bounded number of properties. Specialized structures such as product tiers, webhook destinations and field maps have smaller Actor-specific limits. These checks run even when the input schema cannot express the restriction in Apify's supported dialect.

Dotted paths are literal property navigation. Prototype-related components, executable templates and remote references are rejected. The Actor does not use field names as code. Invalid types and nonfinite numbers are errors instead of implicit string coercions in important metric and limit fields. A buyer should prefer a small normalized export to embedding oversized page documents or unneeded binary content in each row.

The input schema intentionally uses Apify-supported controls rather than unsupported general JSON Schema composition. Runtime validation provides the additional structural guarantees. The build records the output of `apify validate-schema .actor/input_schema.json` for this exact schema. Passing that command confirms the Console schema dialect; it does not establish the correctness of the domain calculations, which are checked separately by the tests and examples.

#### Spending and result reconciliation

Run-start pricing is separate from result pricing. A free result record can still be part of a run whose startup event was charged. The planned tariff uses one named `result-found` event per complete billable unit; a plain dataset push is used for free notices and other explicitly free row types. There is no manual second charge for the same unit, and the automatic dataset-item event must have zero price.

Before a paid push, the Actor reads pricing and the remaining run budget, checks the next combined charge at the microdollar boundary and serializes paid pushes. If pricing is absent on the platform, the automatic dataset-item event is unexpectedly priced, or the budget cannot be read, it fails closed. It does not continue with a guessed budget or announce a clean financial result. The real SDK tests exercise those cases rather than replacing the charging manager with a permissive mock.

An exception during a paid push can occur after the dataset write but before charge confirmation. In that case the Actor reports uncertain delivery and fails the run. A row can therefore exist even when the local paid count was not incremented. Reconcile the actual dataset and platform charge evidence before retrying. Do not infer the bill from raw dataset length when free records or uncertain writes are present.

#### OUTPUT counters

`requested` counts the source input units defined for this Actor. `processed` counts the work units completed by its loop, which can differ from input units for a report builder or a grouped sender. `delivered` counts complete result rows accepted by the delivery guard. `paid` counts confirmed monetized result events; local nonmonetized runs can deliver useful rows with paid zero. `free` counts ordinary dataset pushes, and `failed` counts free rows carrying an error classification.

`unprocessed` records remaining Actor work units, with an Actor-specific reason where a cap or deadline stopped the run. `partial` combines domain incompleteness, free partial rows, a remainder and fatal uncertainty. `fatal` is separate from ordinary per-record errors. `deliveryUncertain` identifies persistence/charge uncertainty at the delivery guard. For webhook API ambiguity, inspect the deliveryStatus record as well. These distinctions are useful when deciding whether to retain complete neighboring rows, fix input or reconcile a run.

#### Diagnosing a changed input shape

First reproduce the issue with a few sanitized records from the same export. Check whether the relevant field is absent, null, a stringified number, an object, or located at a different path. Compare the record with the input examples and exact field map contract. A field that visually looks numeric in a spreadsheet can still arrive as a localized string or a blank after CSV conversion. Fix the source conversion or mapping rather than guessing a value downstream.

For a support report, include the Actor version, run reference, sanitized input shape, expected interpretation and actual error/status fields. Remove webhook URLs, signing secrets and unnecessary personal data. If the issue involves a price range, include the exact relevant string and unit; if it involves timing, include the original timestamp and timezone setting; if it involves DNS, include the MX classification rather than a list of unrelated addresses. These details make the discrepancy reproducible without broadening exposure.

### Commercial playbooks

A useful paid workflow starts with a decision that the output can actually support. Decide whether the customer needs a report, a normalized catalog, a delivery receipt or an address-quality observation. Use the matching result unit when estimating volume and price. Avoid promising a broader source collection or account-verification service simply because those words appear in a source platform name. This Actor's value lies in the bounded transformation and evidence it returns.

For an agency or internal service desk, create an intake checklist tied to the documented fields: authorized source, sample record, chosen settings, desired destination for the result and owner of partial/error review. Keep the first run small and preserve it as the agreed output contract. A larger recurring batch should use the same contract with explicit limits. Changes in source coverage, template meaning or record identity deserve a new review, not just a larger cap.

A service team can price its own analysis or integration work separately, but should keep the Actor's event charges visible. This README states the planned Actor tariff and its unit; it does not promise margin, customer revenue, conversion lift or a particular number of commercial outcomes. Where a task requires a human judgement, such as whether a suspicious price merits a quote request or an unknown delivery should be replayed, assign that judgement to the workflow owner.

### FAQ / Limitations

**Does a successful demo prove the source is live?** No. Synthetic and preview runs establish the local computation or rendering contract. The release acceptance needs its own real supplied-data or authorized delivery evidence. Demo markers remain in the data so they cannot quietly become a market observation.

**Can I pass a URL instead of a dataset ID?** No. Dataset ingestion uses an Apify dataset identifier and the buyer's storage access. URLs contained in supplied source rows are data, not generic network inputs. A custom field map changes which properties are read, not which hosts the Actor can visit.

**Why is a useful row marked found=false?** Some useful record types are explicitly free: operational notices, the product Actor's supplier summaries and the webhook Actor's previews. Interpret type and status before treating found as a universal business predicate. For the email Actor, a completed invalid-syntax check is a billable check even though the address itself is invalid.

**Why did the dataset grow by more rows than the number of paid events?** Free result types and notices share the dataset. Conversely, a persistence/charge failure can leave uncertain evidence that requires reconciliation. Compare the Actor's defined paid unit, the found/partial/error contract and actual platform charge receipts.

**Can I increase every limit to avoid partial work?** No. Hard input, structure, domain and message-product caps protect predictable execution. A working maxItems cap can be adjusted within its schema range, but it cannot bypass the hard runtime contract, the buyer's spend limit or the webhook delivery deadline. Split a larger workload intentionally and preserve source indexes in your own orchestration.

**Does the Actor update my original data?** No. It reads supplied data and writes a result dataset plus OUTPUT for this run. A later import into a CRM, catalog or report is an explicit consumer action. Keep the source reference so that normalized values and mapping decisions can be audited.

**Where should I report a bug or request a change?** Use the Actor page's issue channel when it is available. Provide a small sanitized reproduction and the relevant result classifications. Do not post secrets, private webhook destinations or complete personal-data lists in a public issue. The build draft is intended for review before the final Store listing is accepted.

![Run workflow and review boundary](assets/readme-workflow.svg)

### Recorded platform observations (accepted acceptance runs)

The records below are exact; only `checkedAt` differs between repeated runs.

#### OUTPUT from the accepted happy run

Run `PCx49YYbYHIBWD0SW` on build 0.1.2, 2026-09-06, 2 s, Dataset records: 3, charged events: `{"apify-actor-start": 1, "result-found": 3}`.

```json
{
 "requested": 3,
 "processed": 3,
 "unprocessed": 0,
 "checkedDomains": 2,
 "mxRequested": true,
 "demoInput": true,
 "limitReason": "max_items",
 "delivered": 3,
 "paid": 3,
 "free": 0,
 "failed": 0,
 "partial": false,
 "fatal": "",
 "deliveryUncertain": 0,
 "budgetExhausted": false,
 "replaySafe": false,
 "replayNote": "Serialize schedules. No exactly-once ledger; reconcile uncertain delivery before replay.",
 "schemaVersion": "1.0",
 "checkedAt": "2026-09-06T02:09:51.591Z"
}
```

#### Correct empty-input record

Run `nmrgf7l1NxtEFT5Jf` on build 0.1.2, 2026-09-06, 2 s, Dataset records: 1, charged events: `{"apify-actor-start": 1, "result-found": 0}`.

```json
{
 "schemaVersion": "1.0",
 "type": "no_data",
 "sourceUrl": null,
 "found": false,
 "status": "no_data",
 "resultCount": 0,
 "partial": false,
 "error": "",
 "warnings": [],
 "checkedAt": "2026-09-06T02:09:54.082Z",
 "evidence": "buyer_supplied_data",
 "confidence": null,
 "action": "review",
 "demo": false,
 "deliverability": "unknown"
}
```

#### Malformed-input record

Run `7Wxq5lyU901T69mlh` on build 0.1.2, 2026-09-06, 1 s, Dataset records: 1, charged events: `{"apify-actor-start": 1, "result-found": 0}`.

```json
{
 "schemaVersion": "1.0",
 "type": "rejected_input",
 "sourceUrl": null,
 "found": false,
 "status": "error",
 "resultCount": 0,
 "partial": true,
 "error": "invalid_email_input",
 "warnings": [],
 "checkedAt": "2026-09-06T02:09:58.538Z",
 "evidence": "buyer_supplied_data",
 "confidence": null,
 "action": "review",
 "demo": false,
 "inputIndex": 0
}
```

### Result-type examples

These additional records are exact records from the accepted runs above, selected to show distinct result types.

#### not\_checked

```json
{
  "schemaVersion": "1.0",
  "type": "email_validation",
  "sourceUrl": null,
  "found": true,
  "status": "ok",
  "resultCount": 1,
  "partial": false,
  "error": "",
  "warnings": [
    "Provider rules are shape hints, not account registration checks."
  ],
  "checkedAt": "2026-09-06T01:16:01.286Z",
  "evidence": "buyer_address_syntax",
  "confidence": null,
  "action": "review",
  "demo": true,
  "inputIndex": 1,
  "email": "demo@163.com",
  "normalized": "demo@163.com",
  "domain": "163.com",
  "provider": "163",
  "syntaxValid": true,
  "providerRule": "netease_alias_shape",
  "mxStatus": "not_checked",
  "mxHosts": [],
  "disposableFlag": false,
  "deliverability": "unknown",
  "synthetic": true
}
```

#### skipped\_invalid\_syntax

```json
{
  "schemaVersion": "1.0",
  "type": "email_validation",
  "sourceUrl": null,
  "found": true,
  "status": "ok",
  "resultCount": 1,
  "partial": false,
  "error": "",
  "warnings": [
    "Quoted local parts and SMTPUTF8 local parts are outside the supported syntax subset."
  ],
  "checkedAt": "2026-09-06T01:16:01.287Z",
  "evidence": "buyer_address_syntax",
  "confidence": null,
  "action": "review",
  "demo": true,
  "inputIndex": 2,
  "email": "bad-address",
  "normalized": null,
  "domain": null,
  "provider": "other",
  "syntaxValid": false,
  "providerRule": "invalid_or_unsupported_syntax",
  "mxStatus": "skipped_invalid_syntax",
  "mxHosts": [],
  "disposableFlag": false,
  "deliverability": "unknown",
  "synthetic": true
}
```

### 中文说明：QQ、163 邮箱地址检查

默认三个地址都是合成示例，关闭 MX 查询即可离线运行。可选 MX 检查只使用 DNS，不进行 SMTP、HTTP 验证或发送邮件。邮箱格式和域名邮件记录不能证明具体邮箱存在，所以 deliverability 始终为 unknown。DNS 超时及错误输出为免费 partial 行。请保护原始地址与导出数据。

### Sources and release evidence

- [Node DNS promises API](https://nodejs.org/api/dns.html#dnspromisesresolvemxhostname)
- Accepted replacement specification: WAVE\_B\_PLAN.md §8
- Provider rules: Local conservative shape hints in main.js; no provider registration API

Accepted platform runs on Apify's network (build 0.1.2, 2026-09-06): happy `PCx49YYbYHIBWD0SW`, empty input `nmrgf7l1NxtEFT5Jf`, cap notice `LVBAF9IzP9WmRQdKv`, malformed input `7Wxq5lyU901T69mlh`, inline:live DNS MX observation on qq.com and 163.com `SugZe8FMoPDHLlmQE`. Charged event counts were read back from the run records; local SDK 3.7.2 tests and proofs cover the failure paths that are not reproduced on the platform on purpose.

#### Related tools

Related tools for adjacent workflows in B2B lead generation and data enrichment.

| Actor | What it does |
|---|---|
| [Company Registry Enricher](https://apify.com/zinin/company-registry-enricher) | Pair it in the B2B lead generation and data enrichment workflow: Resolve company names, LEIs, or UK company numbers against official GLEIF and optional Companies House data |
| [Email Domain & MX Verifier for Lead Lists](https://apify.com/zinin/email-verifier) | Pair it in the B2B lead generation and data enrichment workflow: Clean lead lists with syntax, DNS, MX, provider, disposable, role-account, and catch-all-risk evidence |
| [Lead List Cleaner - Dedupe, DNS & Email Hygiene](https://apify.com/zinin/lead-list-cleaner) | Pair it in the B2B lead generation and data enrichment workflow: Normalize mixed lead rows, deduplicate domains and emails, check DNS/MX evidence, score retained leads,... |
| [B2B Lead Enricher](https://apify.com/zinin/b2b-lead-enricher) | Pair it in the B2B lead generation and data enrichment workflow: Turn up to 100 authorized public company websites into evidence-linked research cards: visible technology... |
| [Company Hiring Radar](https://apify.com/zinin/company-hiring-radar) | Pair it in the B2B lead generation and data enrichment workflow: Turn buyer-supplied Greenhouse, Lever, or Ashby board tokens into evidence-backed public hiring... |

# Actor input Schema

## `emails` (type: `array`):

Replace the clearly marked synthetic prefill with your own authorized data. Explicit datasetId suppresses unchanged demo rows. Runtime: 10 MiB input, 64 KiB per row, depth 16 and 100 items per nested array.

## `datasetId` (type: `string`):

Optional Apify dataset ID in your account. Read in pages of 100, at most 5000 rows and 10 MiB. No dataset URL. Remove custom inline rows when setting this. Dataset access is granted by selecting the resource in the resource picker.

## `field` (type: `string`):

Dotted field path to the address in dataset records; inline emails are strings.

## `checkMx` (type: `boolean`):

Use DNS MX only, timeout 5 seconds, concurrency 3 and at most 100 unique domains. Prefill disables DNS for offline demonstration.

## `maxItems` (type: `integer`):

Working output cap; remaining units are reported as unprocessed in OUTPUT. Error notices and free supplier summaries are additional.

## Actor input object example

```json
{
  "emails": [
    "demo@qq.com",
    "demo@163.com",
    "bad-address"
  ],
  "datasetId": "buyerDatasetId",
  "field": "email",
  "checkMx": true,
  "maxItems": 1000
}
```

# Actor output Schema

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

No description

## `summary` (type: `string`):

No description

# 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 = {
    "emails": [
        "demo@qq.com",
        "demo@163.com",
        "bad-address"
    ],
    "field": "email",
    "checkMx": false,
    "maxItems": 1000
};

// Run the Actor and wait for it to finish
const run = await client.actor("zinin/qq-163-chinese-email-address-validator").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 = {
    "emails": [
        "demo@qq.com",
        "demo@163.com",
        "bad-address",
    ],
    "field": "email",
    "checkMx": False,
    "maxItems": 1000,
}

# Run the Actor and wait for it to finish
run = client.actor("zinin/qq-163-chinese-email-address-validator").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 '{
  "emails": [
    "demo@qq.com",
    "demo@163.com",
    "bad-address"
  ],
  "field": "email",
  "checkMx": false,
  "maxItems": 1000
}' |
apify call zinin/qq-163-chinese-email-address-validator --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,zinin/qq-163-chinese-email-address-validator"
        }
    }
}
```

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/61mVV5s2ftJORmTjs/builds/5j4sQc9vyEhdv8hAG/openapi.json
