Skip to content
pdfconversionapi

Extract email attachments API

POST a message to /v2/convert/email/to/extract and each attachment comes back as a separate file in Files, decoded from the message. The roof inspection sample returned five: the inline roof plan, a PDF estimate, a calendar invite and two photos that were both named image.jpg.

Endpoint
POST /v2/convert/email/to/extract
Accepts
email
Sample timing
1 ms median of 5 runsHow we measure
Request size
Up to 10 MB on the Free plan or 100 MB on paid plans, all files together

Request and response

Every sample below ran against the API before this page was published. Set PDFCONVERSIONAPI_KEY to your key and run it as is.

curl -sS -X POST \
  https://api.pdfconversionapi.com/v2/convert/email/to/extract \
  -H "Authorization: Bearer $PDFCONVERSIONAPI_KEY" \
  -F File=@roof-inspection-418-linden.eml \
  -o response.json

# FileData is base64. Save the first file in converted/,
# or print the JSON error.
if jq -e '.Files' response.json > /dev/null; then
  mkdir -p converted
  name=$(jq -r '.Files[0].FileName' response.json)
  jq -r '.Files[0].FileData' response.json \
    | base64 --decode > "converted/$name"
else
  cat response.json
fi
200 OK1 ms median5 files, 50 KB
{
  "ConversionCost": 1,
  "ConversionTime": 0.001,
  "Files": [
    {
      "FileName": "roof-diagram.png",
      "FileExt": "png",
      "FileSize": 6292,
      "FileData": "iVBORw0KGgoAAAANSUhEUgAA…"
    },
    {
      "FileName": "estimate-R-3318.pdf",
      "FileExt": "pdf",
      "FileSize": 29183,
      "FileData": "JVBERi0xLjcKJYCAgIAKMSAw…"
    },
    {
      "FileName": "follow-up-visit.ics",
      "FileExt": "ics",
      "FileSize": 513,
      "FileData": "QkVHSU46VkNBTEVOREFSDQpW…"
    },
    … 2 more files
  ]
}

Response from our test run on 2026-11-03. ConversionTime shows the median of 5 runs. FileData is shortened here; the real response carries the whole file as base64.

The sample, converted

The same file the code sends. Download the input and the output and compare them yourself.

First page of roof-diagram.png, rendered by the API
Input
roof-inspection-418-linden.eml (77 KB)
Output
roof-diagram.png (6.1 KB)estimate-R-3318.pdf (28 KB)follow-up-visit.ics (513 bytes)image.jpg (9.7 KB)image-2.jpg (5.0 KB)
Source
A fictional two-page roof inspection email with an inline roof plan and four attachments, written and drawn by us for this site. (CC0-1.0). How it was made
Made by
2026-11-04, API build 9eeeb0640aba

Parameters

Send these as form fields next to the file. Names are case-insensitive. If you set one of the documented options below that we don't support yet, the request fails with a 400 that names it instead of returning a file made without it.

ParameterWhat it does
IgnoreInlineAttachmentsboolean, default falsetrue leaves out parts shown inside the body: in an .eml those marked Content-Disposition: inline, in a .msg those with a content ID. The sample then returns four files, without roof-diagram.png.
UseCIDAsFileNameboolean, default falsetrue names a part that has a Content-ID after that ID, keeping the original extension. <roof-diagram.418@ridgeline-roofing.example> becomes roof-diagram.418_ridgeline-roofing.example.png; parts without an ID keep their names.
StoreFileboolean, default falsetrue stores each extracted file for 3 hours and returns one download URL per attachment in Files[].Url, which keeps large attachments out of the JSON.

What carries over, and what doesn't

Kept

  • The files' bytes: all five of the sample's attachments, the PDF estimate included, were byte-identical to the files that went into the message
  • The original file names, including accented ones such as Résumé été.txt and the case of the extension (notes.TXT)
  • Images embedded in the HTML body with cid:, which are returned too unless you send IgnoreInlineAttachments=true
  • Duplicate names, made unique with -2, -3 before the extension: image.jpg and image-2.jpg in the sample, notes-3.TXT in our test
  • Attachments of Outlook .msg files: our site report message gave its chart, CSV and photo
  • Outlook's winmail.dat and S/MIME parts as the files they are; nothing is unpacked or decrypted

Lost or not yet supported

  • Names of attachments that have none. They become attachment, attachment-2 and so on, and the API adds .extract to the file name, while FileExt comes back empty.
  • The .eml extension of a forwarded message. An attached message without a file name returned its raw text as attachment.extract, which you can rename to .eml and send back here.
  • Characters that are unsafe in file names. ../../etc/pass wd?.txt came back as _.._etc_pass wd_.txt; slashes, question marks and other symbols are each replaced by an _ character.
  • Extensions longer than 10 letters or with characters other than ASCII letters and digits, which are shortened or filtered.
  • The message itself: subject, sender and body are not returned here. Use /v2/convert/email/to/metadata for those as JSON.
  • FileName, which is not used here; every file keeps its own name.

Limits

  • One message per request; two files return 400 "this conversion takes exactly one file".
  • Requests up to 10 MB on the Free plan or 100 MB on paid plans, all files and fields together. Inside an .eml, base64 makes each attachment about a third larger.
  • A message with no attachments returns 422 "the input file could not be read: the e-mail has no attachments".
  • A file that is not a message returns 422 "the input file could not be read: this is not an e-mail message"; a damaged .msg returns 422 "the Outlook message is damaged".
  • An attachment is never larger than the message it arrives in, so the request limit above caps every file as attached, and each comes back at the size it was attached.
  • 120 seconds per request.

Where teams use it

Inbound invoices and documents

When suppliers email invoices to an inbox, pass each raw message here and route the returned PDF or XLSX to the parser for its type. Files come back with their own names, so estimate-R-3318.pdf stays estimate-R-3318.pdf.

Converting attachments one by one

The email to PDF and image routes list attachments but never convert them. Extract them here, then send each to its own route, such as a DOCX to /v2/convert/docx/to/pdf, and merge the results if you need one file.

Saving photos from field reports

Two attachments can share a name: the sample carries two photos both called image.jpg. The API keeps both, renaming the second image-2.jpg, so a batch job can store them without one overwriting the other.

Questions

Does it accept both .eml and .msg?

Yes. The route is email/to/extract for both, and the converter tells them apart by the file's first bytes. The sample is an .eml; our Outlook test message, sent to the same URL, returned progress-week-9.png, rfi-log-week-9.csv and east-wall-opening.jpg.

Why is there an image I never attached?

It is probably an inline image: a logo, signature or chart the HTML body shows with cid:. Those are attachments in the message's structure, and the sample's roof plan is one. Send IgnoreInlineAttachments=true to get only the files a person attached.

Are the files changed in any way?

Not their contents. Each file is decoded from the message's transfer encoding (base64 for every attachment in the sample) and returned byte for byte: the roof plan, the PDF estimate, the calendar file and both JPEGs matched the originals, and so did the PDFs and the HTML file in our other test messages. Only names can change, when two collide or contain unsafe characters.

What happens with a forwarded email attached to the message?

Its raw message text is returned as one file. If the part had no file name, as in our test, it is called attachment.extract; rename it to .eml and send it to this route again to get its own attachments, or to /v2/convert/eml/to/pdf to read it.

Can I unpack winmail.dat?

Not here. A winmail.dat from Outlook's rich-text format is returned as a single file, the way it was attached, and its contents are not opened.

Convert your first EMAIL file in a minute.