The X e-mail check answers whether an e-mail address is associated with an X account (the service was called Twitter until 2023). It runs in bulk jobs and returns registered: true, false or null (unknown) for each address, with the time we checked. Nothing is sent to the address, and no username, handle or profile is returned.
What does the X e-mail check tell you?
It tells you whether X has an account that uses this e-mail address. X accounts can be registered with an e-mail address or a phone number. For numbers, use the X number check. Accounts created before the 2023 rename are the same accounts, so the name makes no difference to the answer.
registered: true: an X account is associated with the address.registered: false: a conclusive "no account".registered: null: no conclusive answer (statusandreasonexplain it). Not charged.
Who uses it, and why?
- Sign-up review in batches. A periodic job over new registrations uses an account at a large public platform as one signal that an address is in real use, next to the mailbox check. See OTP and sign-up fraud.
- Lead data quality. It works together with the LinkedIn e-mail check when cleaning older contact records.
What do you get back?
| Field | Type | Meaning |
|---|---|---|
email, email_status | string, enum | Normalized address; valid, invalid_email, duplicate or suppressed |
checks["x.email"].registered | boolean or null | true account exists, false none, null unknown |
…status, …reason | enum, string | Outcome, and why an answer is not conclusive |
…confidence, …checked_at | enum, timestamp | How sure the answer is, and when it was obtained |
…cached, …billed | boolean | Cache hit, and whether it was charged |
How is it billed?
You pay the bulk price per address with a conclusive answer, shown on pricing. You're not charged for inconclusive results (unknown, unsupported country, timeout, invalid, duplicate). The free POST /v1/jobs/estimate call shows the maximum cost first, and cache hits are free.
What are the limits?
The check is bulk only. POST /v1/lookup answers 403 service_disabled ("The check 'x.email' is available in bulk jobs only (POST /v1/jobs)."). Use the code x.email. The aliases x and twitter mean the phone-number check. A job takes up to 50,000 numbers and e-mails, and a request up to 20 checks. Requests that look like sequential number ranges or generated e-mail lists are rejected: 20 or more addresses in one request on one domain that differ only by digits or separators, or, with live keys, 50 or more of one such pattern per account per UTC day.
How do I use it responsibly?
Check addresses your users and customers gave you, for fraud prevention and data quality. We never read mailboxes or send e-mail to the address, and the answer is only yes, no or unknown. Don't use it to find someone's social media account or to build audiences. The acceptable use policy forbids both. People can object through the opt-out form.
Example request
curl https://api.mobilevalidate.com/v1/jobs \
-H "Authorization: Bearer mv_test_publicSandboxn9ZgneuhR1B9CRfKG3fulym" \
-H "Content-Type: application/json" \
-d '{"emails":["[email protected]","[email protected]","[email protected]"],"checks":["x.email"]}'// npm install mobilevalidate · Node.js 20+ · save as check.mjs and run: node check.mjs
import { MobileValidate } from "mobilevalidate";
// Omit apiKey to read MOBILEVALIDATE_API_KEY from the environment.
const mv = new MobileValidate({ apiKey: "mv_test_publicSandboxn9ZgneuhR1B9CRfKG3fulym" });
const { data, error } = await mv.jobs.create({
emails: [
"[email protected]",
"[email protected]",
"[email protected]",
],
checks: ["x.email"],
});
if (error) console.error(error.code, error.message);
else console.dir(data, { depth: null });// Node.js 18+, Deno, Bun or the browser console (ES module: save as .mjs or use "type": "module").
const res = await fetch("https://api.mobilevalidate.com/v1/jobs", {
method: "POST",
headers: {
Authorization: "Bearer mv_test_publicSandboxn9ZgneuhR1B9CRfKG3fulym",
"Content-Type": "application/json",
},
body: JSON.stringify({
emails: [
"[email protected]",
"[email protected]",
"[email protected]",
],
checks: ["x.email"],
}),
});
console.log(res.status, res.headers.get("x-request-id"));
console.dir(await res.json(), { depth: null });# pip install mobilevalidate-sdk
from mobilevalidate import MobileValidate
# Omit api_key to read MOBILEVALIDATE_API_KEY from the environment.
mv = MobileValidate(api_key="mv_test_publicSandboxn9ZgneuhR1B9CRfKG3fulym")
result = mv.jobs.create(
emails=[
"[email protected]",
"[email protected]",
"[email protected]",
],
checks=["x.email"],
)
print(result)<?php
$ch = curl_init('https://api.mobilevalidate.com/v1/jobs');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer mv_test_publicSandboxn9ZgneuhR1B9CRfKG3fulym',
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'emails' => [
'[email protected]',
'[email protected]',
'[email protected]',
],
'checks' => ['x.email'],
]),
]);
$response = curl_exec($ch);
echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), PHP_EOL, $response, PHP_EOL;package main
import (
"fmt"
"io"
"net/http"
"strings"
)
func main() {
body := strings.NewReader(`{"emails":["[email protected]","[email protected]","[email protected]"],"checks":["x.email"]}`)
req, err := http.NewRequest("POST", "https://api.mobilevalidate.com/v1/jobs", body)
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "Bearer mv_test_publicSandboxn9ZgneuhR1B9CRfKG3fulym")
req.Header.Set("Content-Type", "application/json")
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
out, _ := io.ReadAll(res.Body)
fmt.Println(res.Status, string(out))
}require "net/http"
require "json"
uri = URI("https://api.mobilevalidate.com/v1/jobs")
req = Net::HTTP::Post.new(uri)
req["Authorization"] = "Bearer mv_test_publicSandboxn9ZgneuhR1B9CRfKG3fulym"
req["Content-Type"] = "application/json"
req.body = JSON.generate({
"emails" => [
"[email protected]",
"[email protected]",
"[email protected]"
],
"checks" => ["x.email"]
})
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
puts res.code, res.bodyRuns as pasted with the public sandbox key, which answers the test values only. In the SDKs, omit the key to use MOBILEVALIDATE_API_KEY.
Response of GET /v1/jobs/{id}/results (excerpt, test mode: the first item of data):
{
"kind": "email",
"input": "re•••@test.mobilevalidate.com",
"email": "[email protected]",
"email_status": "valid",
"e164": null,
"country": null,
"checks": {
"x.email": {
"service": "x.email",
"status": "completed",
"registered": true,
"attributes": null,
"confidence": "high",
"confidence_score": 0.99,
"checked_at": "2026-09-25T14:25:55.285Z",
"cached": false,
"age_seconds": 0,
"billed": false,
"reason": null,
"poll_after_ms": null
}
},
"test": true
}Try it now
Bulk-only service: create a job via POST /v1/jobs, then read GET /v1/jobs/{id}/results. Every example runs as pasted: it uses the public sandbox key, which answers the test values for free. In the SDKs, omit the key to use MOBILEVALIDATE_API_KEY.
curl https://api.mobilevalidate.com/v1/jobs \
-H "Authorization: Bearer mv_test_publicSandboxn9ZgneuhR1B9CRfKG3fulym" \
-H "Content-Type: application/json" \
-d '{"emails":["[email protected]"],"checks":["x.email"]}'// npm install mobilevalidate · Node.js 20+ · save as check.mjs and run: node check.mjs
import { MobileValidate } from "mobilevalidate";
// Omit apiKey to read MOBILEVALIDATE_API_KEY from the environment.
const mv = new MobileValidate({ apiKey: "mv_test_publicSandboxn9ZgneuhR1B9CRfKG3fulym" });
const { data, error } = await mv.jobs.create({
emails: ["[email protected]"],
checks: ["x.email"],
});
if (error) console.error(error.code, error.message);
else console.dir(data, { depth: null });// Node.js 18+, Deno, Bun or the browser console (ES module: save as .mjs or use "type": "module").
const res = await fetch("https://api.mobilevalidate.com/v1/jobs", {
method: "POST",
headers: {
Authorization: "Bearer mv_test_publicSandboxn9ZgneuhR1B9CRfKG3fulym",
"Content-Type": "application/json",
},
body: JSON.stringify({
emails: ["[email protected]"],
checks: ["x.email"],
}),
});
console.log(res.status, res.headers.get("x-request-id"));
console.dir(await res.json(), { depth: null });# pip install mobilevalidate-sdk
from mobilevalidate import MobileValidate
# Omit api_key to read MOBILEVALIDATE_API_KEY from the environment.
mv = MobileValidate(api_key="mv_test_publicSandboxn9ZgneuhR1B9CRfKG3fulym")
result = mv.jobs.create(
emails=["[email protected]"],
checks=["x.email"],
)
print(result)<?php
$ch = curl_init('https://api.mobilevalidate.com/v1/jobs');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer mv_test_publicSandboxn9ZgneuhR1B9CRfKG3fulym',
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'emails' => ['[email protected]'],
'checks' => ['x.email'],
]),
]);
$response = curl_exec($ch);
echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), PHP_EOL, $response, PHP_EOL;package main
import (
"fmt"
"io"
"net/http"
"strings"
)
func main() {
body := strings.NewReader(`{"emails":["[email protected]"],"checks":["x.email"]}`)
req, err := http.NewRequest("POST", "https://api.mobilevalidate.com/v1/jobs", body)
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "Bearer mv_test_publicSandboxn9ZgneuhR1B9CRfKG3fulym")
req.Header.Set("Content-Type", "application/json")
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
out, _ := io.ReadAll(res.Body)
fmt.Println(res.Status, string(out))
}require "net/http"
require "json"
uri = URI("https://api.mobilevalidate.com/v1/jobs")
req = Net::HTTP::Post.new(uri)
req["Authorization"] = "Bearer mv_test_publicSandboxn9ZgneuhR1B9CRfKG3fulym"
req["Content-Type"] = "application/json"
req.body = JSON.generate({
"emails" => ["[email protected]"],
"checks" => ["x.email"]
})
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
puts res.code, res.body// .mcp.json (Claude Code), ~/.cursor/mcp.json (Cursor) or .vscode/mcp.json ("servers" instead of "mcpServers")
{
"mcpServers": {
"mobilevalidate": {
"type": "http",
"url": "https://mcp.mobilevalidate.com/mcp",
"headers": { "Authorization": "Bearer ${MOBILEVALIDATE_API_KEY}" }
}
}
}
// Then ask your agent, e.g.:
// "Check [email protected] with x.email" → tool: create_lookup_jobThe hosted MCP server is live for customers with an agent key (mv_agent_…) or a test key. Or run it locally with npx -y @mobilevalidate/mcp.
Real test-mode result for this check
{
"service": "x.email",
"status": "completed",
"registered": true,
"attributes": null,
"confidence": "high",
"confidence_score": 0.99,
"checked_at": "2026-09-25T14:25:29.021Z",
"cached": false,
"age_seconds": 0,
"billed": false,
"reason": null,
"poll_after_ms": null
}What you get
Fields of checks["x.email"], generated from the public service catalog.
| Field | Type | Meaning |
|---|---|---|
| registered | boolean | null | true = found, false = not found, null = unknown (not charged). |
| status | enum | completed, pending, unknown, unsupported_country or failed. |
| checked_at | timestamp | When the answer was obtained. |
Frequently asked questions
Does the check return the X username or profile?
No. It only answers whether an X account is associated with the address: yes, no or unknown. No username, handle, photo or profile is returned.
Is X the same as Twitter?
Yes. Twitter was renamed X in 2023. Accounts created under either name are covered.
Why is the X e-mail check only in bulk jobs?
It is offered in bulk jobs only for now. POST /v1/lookup refuses it with service_disabled; use POST /v1/jobs.
What does an unknown answer cost?
Nothing. Inconclusive answers are never charged.

