VCF to JSON Converter — vCard Contact Parser
Pricing
$20.00 / 1,000 parsed files
VCF to JSON Converter — vCard Contact Parser
Convert vCard 3.0/4.0 contact text to JSON for n8n, Make and agents. Keep multiple phones, emails and Apple labels. $0.02 per bundle, up to 500 contacts. Includes API and Python workflow examples.
Pricing
$20.00 / 1,000 parsed files
Rating
0.0
(0)
Developer
Stefan Schellen
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
16 hours ago
Last modified
Categories
Share
Turn vCard 3.0/4.0 contact text into structured JSON for n8n, Make and AI agents. Keep multiple phone numbers, multiple emails and Apple labels, instead of flattening a contact to its first value.
$0.02 per successful bundle — up to 500 contacts and 1 MiB. No additional platform-usage charge. Rejected bundles produce no product event.
Try it in one minute
- Click Try for free and run the synthetic example using available Apify credit; otherwise the displayed price applies.
- Open the dataset: one result contains a
contactsarray. - Use the API example below to connect your workflow. Review warnings before importing contacts anywhere.
When to use it
- Prepare supported contact fields from a VCF export for your own import mapping.
- Keep repeated emails/phones and labels when passing contacts between workflow steps.
- Give an agent a deterministic parser with explicit limits and warnings.
Why pay instead of using a free library? Libraries such as vobject are a valid free option if you can host and maintain Python code. This Actor supplies a hosted API, bounded processing and a stable output envelope. It does not add contact enrichment or recover missing data.
Not suitable for: vCard 2.1, photos, lossless archival conversion or a ready-made CRM connector.
Input and output
Send a JSON object with exactly one field, vcard_text. Example:
{"vcard_text": "BEGIN:VCARD\r\nVERSION:3.0\r\nFN:Zoë Example\r\nN:Example;Zoë;;;\r\nitem1.TEL;TYPE=CELL:+31001\r\nitem1.X-ABLabel:iPhone\r\nTEL;TYPE=WORK:+31002\r\nEMAIL;TYPE=HOME:personal@example.test\r\nEMAIL;TYPE=WORK:work@example.test\r\nEND:VCARD\r\n"}
This example produces one contact with two phones, two emails and an iPhone label. A complete response is shown below.
The result contains schema_version, contact_count, and contacts. Each contact has these field collections:
| Collection | Value |
|---|---|
names | Structured family/given/additional/prefix/suffix |
formatted_names | Full display name, without guessed splitting |
organizations | Ordered organization components |
titles | Job title text |
phones | Original phone text or tel: URI |
emails | Original email text |
addresses | Structured postal-address components |
Each collection has present and items. Each item includes value, types, group, labels, and parameters. Repeated values are preserved, not deduplicated. Names and addresses may have repeated components represented as arrays. completeness reports missing, empty, or present; it is not a check of deliverability or identity.
One dataset item contains the entire result envelope. The same object is stored in key-value record OUTPUT. Split its contacts array in your workflow when you need one item per contact. This envelope makes one run easy to retrieve and avoids duplicated output on a run restart.
Scope and limits
- Maximum 1 MiB UTF-8 input, 500 contacts, 256 properties/contact, 8 KiB unfolded line, 4 MiB compact JSON output.
- Supports the documented contact fields in 3.0/4.0, folding, Unicode, escapes and Apple group labels. Not a full RFC conformance validator.
- Version 2.1 and legacy
CHARSET/ENCODINGon supported fields are rejected. Convert those files elsewhere first. - Photos are omitted with
PHOTO_OMITTED. Other fields such as NOTE, URL and birthday are omitted; their names appear inunmapped_propertieswithUNMAPPED_PROPERTIES. - No photo/URL fetching, OCR, contact enrichment, CRM updates or inferred first/last names. This is not a lossless conversion of every vCard property.
- A malformed or over-limit bundle fails as a whole. Partial contacts are not returned or billed.
The parser worker has a 5-second wall timeout. On Linux it also has a 4-second CPU limit and 160 MiB address-space limit. The deployed Actor allocation is 256 MiB and 20 seconds total. Normal Linux cloud runs and build-time adversarial checks of memory, CPU and parent wall timeout passed.
Errors
Failed runs have a fixed status code and, for rejected input, an ERROR record. Common codes: UNSUPPORTED_VERSION, INPUT_LIMIT, LINE_LIMIT, CONTACT_LIMIT, PROPERTY_LIMIT, OUTPUT_LIMIT, UNTERMINATED_CARD, MALFORMED_PROPERTY, PARSE_ERROR, LEGACY_ENCODING_UNSUPPORTED, PROCESSING_TIMEOUT. Values from the submitted contact are not included in those errors.
Workflow integration
Use Apify's synchronous dataset endpoint:
POST https://api.apify.com/v2/acts/creatieve_naald~vcard-to-json/run-sync-get-dataset-items?memory=256&timeout=20&maxTotalChargeUsd=0.02
Authenticate using an Authorization: Bearer … header stored in your workflow's credentials. Do not put tokens in query strings, shared workflow exports or contact data. The JSON body is the input object. The HTTP result is an array containing one result envelope; iterate over its contacts array.
For n8n, use an HTTP Request node with POST, a stored Header Auth credential and JSON body {{ {vcard_text: $json.vcard_text} }}. Set a 30-second request timeout. Disable automatic retries that start a fresh run. Then use Split Out on contacts. The equivalent authenticated HTTP roundtrip has been tested. These n8n settings are a configuration recipe; a full n8n workflow import has not been verified.
For Make, use HTTP → Make a request, POST, the same URL and a stored Bearer credential. Build the body with JSON → Create JSON so line breaks and quotes are escaped correctly; map the text into vcard_text. Parse the response, then iterate the first envelope's contacts. This is a configuration recipe, not a tested Make blueprint.
For an agent, call the same HTTP endpoint with structured JSON input. Do not ask an LLM to reconstruct missing names. Inspect warnings before using the result.
A new API call is a new job and can be billed again. On a network timeout, inspect/retrieve the existing run before starting another. Automatic resumption of the same run uses its stored result and billing state.
Price and data handling
Price: $0.02 per successfully processed file/bundle, independent of contact count within the limits. One parsed-file event; no automatic start/dataset charges or additional platform-usage charge. A rejected bundle produces no product event. The minimum permitted maximum charge per run is $0.02.
Apify receives input and stores the run's input/output and dataset according to account/platform settings. This is not a zero-retention service. The parser subprocess receives no account credentials and does not fetch external resources. Application status messages contain counts/codes only. Console retention is 7 days for unnamed storage/other runs, with the latest 10 runs retained for 4 months. An unauthenticated dataset request returned 403; unauthenticated INPUT, OUTPUT and JOB_STATE requests also returned 403. Anyone holding a valid signed sharing link may still access its target; do not share those links.
Copyable Python workflow: VCF to contact rows
The example below calls Apify once and keeps every phone/email in arrays. Read a UTF-8 file locally; the Actor takes text, not a file URL. Set APIFY_TOKEN through your secret manager. Python 3 standard library only. The mapping and HTTP request construction were tested locally against saved acceptance output; this is not a verified n8n/Make import.
"""Two copyable workflow adapters. No network calls on import."""import json, os, urllib.requestfrom pathlib import Pathdef call_actor(slug, field, text, price):url = f'https://api.apify.com/v2/acts/creatieve_naald~{slug}/run-sync-get-dataset-items?memory=256&timeout=20&maxTotalChargeUsd={price}'request = urllib.request.Request(url, data=json.dumps({field: text}).encode(), headers={'Authorization': 'Bearer '+os.environ['APIFY_TOKEN'], 'Content-Type': 'application/json'}, method='POST')# A timeout can mean the job ran. Never automatically start a replacement job.with urllib.request.urlopen(request, timeout=35) as response:rows = json.load(response)if not isinstance(rows,list) or len(rows)!=1 or rows[0].get('schema_version')!='1.0':raise ValueError('Unexpected response; inspect existing run before retrying')return rows[0]def contact_rows(envelope):"""One row/contact, keeping every phone and email in arrays."""return [{'name': [x['value'] for x in c['fields']['formatted_names']['items']],'phones': [x['value'] for x in c['fields']['phones']['items']],'emails': [x['value'] for x in c['fields']['emails']['items']],'warnings': c['warnings'], 'original': c} for c in envelope['contacts']]result = call_actor('vcard-to-json', 'vcard_text',Path('contacts.vcf').read_text(encoding='utf-8'), '0.02')rows = contact_rows(result)# Send rows to your chosen destination after checking warnings.
Guide: preserve repeated contact values in your destination
Use the Python workflow above to produce one row per contact. Each row keeps phones and emails as arrays, plus warnings and the complete original contact. Review warnings before importing.
- Map arrays into a destination that supports multiple values, or keep a linked child table.
- Do not repeatedly overwrite one CRM phone field: the last assignment will erase earlier values even if parsing preserved them.
- Retain
originalwhen you need Apple labels or other source fields later. - Test with synthetic contacts containing two phones, two emails and a label before using personal data.
Our offline fixture verifies those repeated values and the Apple label. Your CRM integration is not tested by this example. A timed-out API call may already have completed: inspect its existing run before retrying.
More bounded workflow tools: Agent Tools portfolio.
Frequently asked questions
Is the price per contact? No: one successful bundle of 1–500 contacts costs $0.02 within the documented limits. 100 successful bundles cost $2, before any applicable taxes or account-specific discounts.
Does it clean or verify addresses and emails? No. It parses supported fields and reports omissions; it does not verify deliverability or guess missing values.
Can it take a file URL? No. Read your UTF-8 VCF file in your workflow and submit its text as vcard_text.
Complete example response
The synchronous HTTP endpoint returns an array containing this envelope:
[{"schema_version": "1.0","contact_count": 1,"contacts": [{"index": 1,"version": "3.0","fields": {"names": {"present": true,"items": [{"value": {"family": "Example","given": "Zoë","additional": "","prefix": "","suffix": ""},"types": [],"group": null,"labels": [],"parameters": {}}]},"formatted_names": {"present": true,"items": [{"value": "Zoë Example","types": [],"group": null,"labels": [],"parameters": {}}]},"organizations": {"present": false,"items": []},"titles": {"present": false,"items": []},"phones": {"present": true,"items": [{"value": "+31001","types": ["CELL"],"group": "item1","labels": ["iPhone"],"parameters": {"TYPE": ["CELL"]}},{"value": "+31002","types": ["WORK"],"group": null,"labels": [],"parameters": {"TYPE": ["WORK"]}}]},"emails": {"present": true,"items": [{"value": "personal@example.test","types": ["HOME"],"group": null,"labels": [],"parameters": {"TYPE": ["HOME"]}},{"value": "work@example.test","types": ["WORK"],"group": null,"labels": [],"parameters": {"TYPE": ["WORK"]}}]},"addresses": {"present": false,"items": []}},"unmapped_properties": [],"warnings": [],"completeness": {"names": "present","formatted_names": "present","organizations": "missing","titles": "missing","phones": "present","emails": "present","addresses": "missing"}}]}]
Support
Report reproducible problems within the documented scope through this Actor's Apify Issues tab. Include only synthetic examples and remove contact details, API tokens and signed links. Custom CRM integrations, enrichment and vCard 2.1 conversion are outside the scope.