Contact Form Detector: Fields, CAPTCHA & Action URL avatar

Contact Form Detector: Fields, CAPTCHA & Action URL

Pricing

from $1.40 / 1,000 website with a contact page or forms

Go to Apify Store
Contact Form Detector: Fields, CAPTCHA & Action URL

Contact Form Detector: Fields, CAPTCHA & Action URL

Give company websites, get the contact page URL, whether it has a contact form, each field and its role (name, email, phone, message), the form action, CAPTCHA (reCAPTCHA, hCaptcha, Turnstile), honeypots, and emails and phones. Never submits a form.

Pricing

from $1.40 / 1,000 website with a contact page or forms

Rating

0.0

(0)

Developer

NeverEmpty

NeverEmpty

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

3 days ago

Last modified

Share

Contact Form Detector - find the contact page and read its form fields, CAPTCHA and action URL

For outreach teams, lead-gen agencies and form-automation builders: give it a list of company websites and get back, for each one, the contact page URL and the exact shape of its contact form - every field with its name, type, label and role (firstName, email, phone, company, message, ...), which fields are required, where the form posts (action, method, encoding), which CAPTCHA is on the page (reCAPTCHA v2/v3, hCaptcha, Turnstile) and which fields are hidden spam traps.

One row per website. Also on the row: emails, phone numbers and social profiles printed on those pages, forms embedded from HubSpot, Typeform, Google Forms, Jotform and others, and the site's own "no sales pitches through this form" notice when it has one.

Finds contact pages in 20+ languages - Contact, Kontakt, Contacto, Contato, Contatti, Nous contacter, お問い合わせ, 联系我们, 문의, Связаться, İletişim, Liên hệ and more - by reading the links on the home page, then the common contact paths for the site's language.

Reads only. No form is ever submitted, no CAPTCHA is solved, robots.txt is followed. No API key, no login. Export as JSON, CSV or Excel.

What you get

One row per website, with:

  • The contact page: contactPageUrl, how it was found (link, link-from-contact-page, common-path, given-url) and the link text the site uses for it
  • The form: hasContactForm, contactFormUrl, formAction, formMethod, formEnctype, whether the action posts to the same site, the submit button label, and the form builder behind it (contact-form-7, wpforms, gravity-forms, elementor, hubspot, shopify, ...)
  • Every field in formFields: name, type, label, placeholder, required or not, the options of a select box, and a role you can map from
  • fieldMap: role → field name, ready to fill ({"email": "your-email", "message": "your-message"}), and requiredRoles
  • What stands in the way: captcha (recaptcha-v2, recaptcha-v3, recaptcha-enterprise, hcaptcha, turnstile, captcha-field), honeypotFields, consentRequired (a privacy checkbox that must be ticked), fileUpload
  • Forms from other services: embeddedForms with the provider, its URL and how it is placed (iframe, link, script)
  • noSolicitationNotice: the sentence where the site asks not to be sent sales messages, with the page it is on - so you can respect it
  • Other ways to reach them: emails, phones (international format) and phonesAsWritten, socials (LinkedIn, X/Twitter, Facebook, Instagram, YouTube, TikTok, GitHub), contact pages on another domain (offsiteContactLinks)
  • The trail: pagesRead, httpStatus, status and a plain-English note that says why a site has no row of results

Example output (one website, socials shortened)

{
"status": "ok",
"website": "https://wpforms.com/",
"contactPageUrl": "https://wpforms.com/contact/",
"contactPageFoundBy": "link",
"contactLinkText": "contact us",
"hasContactForm": true,
"contactFormUrl": "https://wpforms.com/contact/",
"formProvider": "wpforms",
"formAction": "https://wpforms.com/contact/",
"formActionSameSite": true,
"formMethod": "POST",
"formEnctype": "multipart/form-data",
"formFields": [
{ "name": "wpforms[fields][0][first]", "type": "text", "role": "firstName", "label": "First", "required": true },
{ "name": "wpforms[fields][0][last]", "type": "text", "role": "lastName", "label": "Last", "required": true },
{ "name": "wpforms[fields][1]", "type": "email", "role": "email", "label": "Email: *", "required": true },
{ "name": "wpforms[fields][3]", "type": "url", "role": "website", "label": "Website: *", "required": true },
{ "name": "wpforms[fields][2]", "type": "textarea", "role": "message", "label": "Comment or Message *", "required": true }
],
"fieldMap": {
"firstName": "wpforms[fields][0][first]",
"lastName": "wpforms[fields][0][last]",
"email": "wpforms[fields][1]",
"website": "wpforms[fields][3]",
"message": "wpforms[fields][2]"
},
"requiredRoles": ["firstName", "lastName", "email", "website", "message"],
"captcha": [],
"honeypotFields": [],
"consentRequired": false,
"fileUpload": false,
"submitLabel": "Submit",
"embeddedForms": [],
"noSolicitationNotice": null,
"socials": { "twitter": ["http://x.com/easywpforms"], "facebook": ["https://facebook.com/wpforms"] },
"pagesRead": ["https://wpforms.com/", "https://wpforms.com/contact/"]
}

Field roles: name, firstName, lastName, nameKana, email, phone, company, jobTitle, website, subject, message, address, city, zip, country, date, number, consent, file, captchaAnswer, other. Labels in Japanese, German, French, Spanish, Portuguese, Italian and other languages are read too (会社名 → company, Nachricht → message).

Input

  • Websites (domains) - domains (example.com) or URLs, one per line, up to 500 per run. Give a page URL (https://example.com/de/kontakt) and that page is read first.
  • Pages read per website (maxPagesPerWebsite, 1 to 8, default 4) - the home page counts as one. Reading stops as soon as a contact form is found.
  • Websites read at the same time (maxConcurrency, 1 to 8, default 4).
  • Time limit per request (requestTimeoutSecs, 5 to 60 seconds, default 20). Busy servers (HTTP 429 and 5xx) are asked again up to two more times.

Leave the list empty and the example site contactform7.com is read, free of charge.

When a website has no row of results

Every website you give gets a row, so you can see what happened. status says why:

statuswhat it means
oka contact page or a contact form was found
no-contact-page-foundthe pages read have no contact link and the usual contact paths do not answer with a contact page
robots-disallowed / robots-unreachablethe site's robots.txt does not allow the page, or robots.txt itself could not be read - the page is not fetched
login-requiredthe site sends visitors to a login page
blockedthe site answers with a bot check or refuses the request
not-found, http-error, unreachable, unreadable, not-htmlthe site is gone, answers with an error, cannot be reached, or is not a web page
bad-inputnot a website (an email address, an ftp link, a private address)

Only ok rows are charged.

Things this Actor is careful about

  • It never submits a form and never solves a CAPTCHA. It reports what is there.
  • robots.txt is followed for every page, and a site whose robots.txt cannot be read is left alone.
  • A page is called a contact page only when it is one. A path that answers 200 for anything (/contact/ on a site that returns its home page for every URL) is checked for contact wording and a form before it is used. Links that lead to another company's site are not followed.
  • Spam traps are named, not filled. Hidden fields named like a trap (website_hp, honeypot, ak_hp_textarea, ...) are listed in honeypotFields so your own tooling can leave them empty.
  • Search boxes, newsletter sign-ups and login forms are not counted as contact forms.
  • The same website given twice is read and charged once, and a run restarted by the platform does not charge again for websites already returned.

Good to know

  • Forms that a script draws after the page loads are not in the page's HTML. When the contact page loads a form script (HubSpot, Marketo, Pardot, ...) or has almost no text without JavaScript, jsFormHint says so and embeddedForms lists the script.
  • Some contact pages carry only an email address or a phone number. Those rows have hasContactForm: false and the address and number filled in.
  • A big site can have several forms (sales, support, press). The main one is in formFields; otherContactForms counts the rest.

Frequently asked

How do I find the contact form of a list of company websites? Paste the domains into Websites and run. Each row gives the contact page URL, the form's fields with their roles, and where it posts.

How do I know which fields a contact form requires? requiredRoles lists the roles of the required fields, and each entry of formFields has required: true or false.

Which websites use reCAPTCHA, hCaptcha or Cloudflare Turnstile on their contact form? The captcha column names the kind found on the page with the form. An empty list means no CAPTCHA mark was found in the HTML.

Can it find contact pages on German, Japanese or Spanish sites? Yes. Links such as Kontakt, お問い合わせ, Contacto, Contato and Contatti are recognised, and the usual contact path for the site's language is tried when no link is found.

Does it send messages through the forms? No. It reads the form and returns its shape. Sending is up to you, and noSolicitationNotice shows when a site asks not to receive sales messages.

Does it need a browser or an API key? No API key and no login. Pages are read as plain HTML, which keeps runs fast and inexpensive.

Limitations

  • Fields that only exist after JavaScript runs are not read (see jsFormHint).
  • Sites behind a bot check, a login, or a robots.txt rule are reported with their status and not read.
  • Up to 8 pages per website are read; a form hidden deeper is not found.
  • CAPTCHA detection is from marks in the HTML; a CAPTCHA added only after a click is not seen.

Pricing

You pay per website where a contact page or a contact form was found (status: ok). Websites with nothing found, errors, blocked sites and the free example run are not charged. Set a maximum charge for a run and the Actor stops before going over it, and says how many websites were left out.

Thanks for using this Actor

Found a site it reads wrong, or a field role you need? Open an issue on the Actor's page - fixes for reported sites come first.