Watch a Folder and Auto-Convert New Files

By FileConvertLab

Published:

Watch folder auto-convert: fcl watch converts files dropped into an inbox folder into out, and a watcher script also sorts the originals into done and failed
Diagram of a watch folder: fcl watch converts new files from inbox into out, and a watcher script around fcl convert can also move the originals to done or failed

Want a watch folder that auto-converts every file dropped into it? Run fcl watch ~/inbox --to pdf --out-dir ~/converted and leave it running. Each new file in ~/inbox gets uploaded, converted and saved to ~/converted, and it keeps going until you press Ctrl+C. When you also want processed files moved out of the way, failures sorted into their own folder and rate-limited files retried, wrap fcl convert in a short watcher script instead. There's one below for Linux (inotifywait), macOS (fswatch) and Windows (PowerShell FileSystemWatcher).

We ran every command in this post while writing it: fcl 0.1.0 against the conversion service, inotifywait 4.23, fswatch 1.14 and PowerShell 7.6 on Linux. The flag reference lives on the fcl CLI reference page. If fcl isn't installed yet, start with the fcl CLI quickstart.

Auto-Convert a Watch Folder with fcl watch

$ fcl watch inbox --to pdf --out-dir out --interval 2
Watching inbox for new files to convert to pdf.
1 files already there are left alone. Press Ctrl+C to stop.
Saved to out/inv1.pdf
Saved to out/inv2.pdf

--to takes the same targets as fcl convert: pdf, word, excel, png, jpg, webp, searchable-pdf, ocr-word, txt and the rest in the formats table on the reference page. The operation is picked per file from its extension, so a folder that receives .docx and .xlsx files can watch with --to pdf and each file goes through the right conversion.

The flags that matter for a watch folder:

FlagWhat it doesDefault
--out-dir DIRWhere results goNext to the input file
--interval NSeconds between folder checks5
--limit NStop after N successful conversions0 (keep watching)
--quality, --dpi, --ocr-language, --optionPassed to every conversionOperation defaults

Add the global --json flag and each event becomes one JSON line on stdout, which is easy to feed into a log collector:

$ fcl --json watch inbox --to pdf --out-dir out --interval 2
{"directory":"inbox","event":"watching","existingFiles":1,"intervalSeconds":2,"to":"pdf"}
{"input":"inbox/inv1.docx","jobId":"job_c80baf3722e6","operation":"word-to-pdf","output":"out/inv1.pdf","status":"completed","warnings":[]}

A file that can't be converted produces a line with "status":"failed" and an error field, and the watcher carries on with the next file.

What fcl watch Does and Doesn't Do

We tested the edge cases. Here's how fcl watch behaves:

  • Files already in the folder are ignored. Only files that appear after start are converted. Anything that arrived while the watcher was stopped counts as "already there" at the next start
  • A new file waits one check before upload. Its size has to stay the same across two checks, so a file that's still being copied isn't sent half-written. With --interval 2 that's a 2-second quiet period
  • Only the top level of the folder is watched. Files in subfolders are skipped
  • Results never trigger a second conversion. If output goes into the watched folder, fcl remembers the files it wrote and leaves them alone
  • Each file name is converted once per session. Drop scan.pdf in, move it out, drop a new scan.pdf in: the second one is ignored until you restart the watcher. That matters for scanners that always write the same name
  • Failures aren't retried. An unsupported extension, a corrupt file or a rate-limit error is logged, and that file is skipped for the rest of the session
  • Source files stay where they are. fcl watch doesn't move or delete anything

For a folder that receives a few uniquely named files an hour, like invoices saved from email or exports from an app, those rules are fine. If you need a done/failed split, retries, or scanner files that reuse names, use one of the scripts further down.

Watch a Scanner Folder: Scans to Searchable PDF

The most useful watch folder is often the one your scanner saves into. Point fcl watch at it with an OCR target, and every scan comes out as a PDF you can search and copy text from:

$ fcl watch scans --to searchable-pdf --ocr-language de --out-dir scans/searchable
Watching scans for new files to convert to searchable-pdf.
0 files already there are left alone. Press Ctrl+C to stop.
Saved to scans/searchable/scan1.pdf

$ pdftotext scans/searchable/scan1.pdf -
Rechnung Nr. 4711
Betrag: 120 Euro

searchable-pdf accepts PDF, PNG and JPG scans. --ocr-language takes one of 29 language codes (de, fr, ja, ar and so on). Leave it out and recognition runs in automatic mode. On the website, the same conversion is OCR PDF to searchable PDF. Want editable text instead? Watch with --to ocr-word to get DOCX files.

Mind the credits here: OCR costs 5 credits per file. A guest gets 50 credits a day, which is 10 scans. A free account gets 200 a day.

Watch Folder Limits: Credits per Minute and per Day

Every conversion spends credits, and a watch folder can spend them faster than you expect. Here's what each plan allows:

PlanCredits per minuteCredits per day
Guest (no login)2050
Free account50200
Premium1002,000
Premium Plus2005,000

A Word, Excel or PowerPoint to PDF job costs 2 credits, most image format changes cost 2, PDF to image and image compression cost 1, OCR costs 5 and video conversion costs 10.

Here's what happens when a burst lands. We copied 12 DOCX files into a folder watched by fcl watch --to pdf as a guest. The first 10 converted. Files 11 and 12 failed with Rate limit exceeded. This operation costs 2 credits, you have 0 credits/min remaining. Try again in 39 seconds., and because fcl watch doesn't retry, they stayed unconverted. The --interval flag doesn't help here, because it spaces out folder checks, not uploads.

So:

  • Sign in with fcl login for the higher allowance. A service started under your user account picks up the stored session
  • Retry rate-limited files after a minute. The scripts below do this
  • Watch the daily cap for OCR and video folders. A daily-limit error means waiting until the next day or moving up a plan, and retrying won't help

Linux: Watch a Folder and Convert with inotifywait

inotifywait (package inotify-tools) reports file events as they happen. This script watches <folder>/inbox, converts each finished file, moves the original to done/ or failed/, and retries rate-limited files up to three times:

#!/usr/bin/env bash
set -u
DIR=${1:?folder}; TO=${2:?target format}
mkdir -p "$DIR/inbox" "$DIR/out" "$DIR/done" "$DIR/failed"

convert_with_retry() {
  local src=$1 result
  for attempt in 1 2 3; do
    result=$(fcl --json convert "$src" --to "$TO" --out-dir "$DIR/out") && return 0
    echo "$(date -Is) $result" >>"$DIR/errors.log"
    case "$result" in
      *"Rate limit exceeded"*) sleep 60 ;;
      *) return 1 ;;
    esac
  done
  return 1
}

process() {
  local name=$1 src="$DIR/inbox/$1"
  case "$name" in .*|*.part|*.crdownload|*.tmp) return ;; esac
  [ -f "$src" ] || return
  if convert_with_retry "$src"; then
    mv -f -- "$src" "$DIR/done/"
  else
    mv -f -- "$src" "$DIR/failed/"
  fi
}

for leftover in "$DIR"/inbox/*; do
  [ -e "$leftover" ] && process "$(basename "$leftover")"
done

inotifywait -m -q -e close_write -e moved_to --format '%f' "$DIR/inbox" |
while IFS= read -r name; do
  process "$name"
done

Run it with ./watch-convert.sh ~/hotfolder pdf. What each part is for:

  • close_write and moved_to fire when a writer closes the file or a file is moved in. So you never pick up a file mid-copy, and you don't need a size-polling delay
  • The leftover loop converts whatever landed while the script wasn't running, which fcl watch skips
  • Moving to done/ means the same name can arrive again later and it'll be converted again
  • Temporary names are skipped. Browsers and many sync tools write .part or .crdownload files first and rename them when finished, and the rename triggers moved_to
  • fcl --json returns the error text in the output, so the script can tell a rate limit (wait and retry) from a bad file (give up)

In our test, 12 DOCX files dropped at once as a guest all converted. Two hit the per-minute limit, waited 60 seconds and went through on the second attempt. File names with spaces work too. A file that doesn't convert, such as an unsupported .xyz, ends up in failed/, and the reason is in errors.log.

macOS: Watch a Folder with fswatch

macOS has no inotifywait. Install fswatch with Homebrew (brew install fswatch). It reports file-system events, but it can report a file as soon as it's created, before the copy finishes. So this version waits until the size stops changing:

#!/usr/bin/env bash
set -u
DIR=${1:?folder}; TO=${2:?target format}
mkdir -p "$DIR/inbox" "$DIR/out" "$DIR/done" "$DIR/failed"

wait_until_stable() {
  local size=-1 now
  while now=$(wc -c <"$1" 2>/dev/null) && [ "$now" != "$size" ]; do
    size=$now; sleep 2
  done
}

fswatch -0 --event Created --event Updated --event MovedTo --event Renamed "$DIR/inbox" |
while IFS= read -r -d '' src; do
  name=$(basename "$src")
  case "$name" in .*|*.part|*.crdownload|*.tmp) continue ;; esac
  [ -f "$src" ] || continue
  wait_until_stable "$src"
  [ -f "$src" ] || continue
  if fcl convert "$src" --to "$TO" --out-dir "$DIR/out" 2>>"$DIR/errors.log"; then
    mv -f -- "$src" "$DIR/done/"
  else
    mv -f -- "$src" "$DIR/failed/"
  fi
done

fswatch sends several events for one file. That's harmless here: once the file has moved to done/, the [ -f "$src" ] check skips the rest. The stability check is a heuristic. A copy from a slow network share that stalls for more than 2 seconds can still be caught halfway. When you control the program that writes the files, have it write under a .part name and rename at the end. For rate-limit retries, drop in the convert_with_retry function from the Linux script.

We ran this script with fswatch on Linux, where it uses inotify. On a Mac it uses its FSEvents backend, which reports creates, updates and renames under the same event names.

Windows: Watch a Folder with PowerShell FileSystemWatcher

PowerShell can use .NET's FileSystemWatcher directly. This script waits for a create or rename event (or 5 seconds), then processes everything in inbox that's ready:

param(
  [Parameter(Mandatory)] [string] $Folder,
  [Parameter(Mandatory)] [string] $To
)
$inbox, $out, $done, $failed = 'inbox', 'out', 'done', 'failed' |
  ForEach-Object { New-Item -ItemType Directory -Force -Path (Join-Path $Folder $_) } |
  ForEach-Object FullName
$log = Join-Path $Folder 'errors.log'

function Test-Ready([string] $Path) {
  try { [IO.File]::Open($Path, 'Open', 'Read', 'None').Dispose(); $true } catch { $false }
}

$watcher = [IO.FileSystemWatcher]::new($inbox)
while ($true) {
  $null = $watcher.WaitForChanged([IO.WatcherChangeTypes]::Created -bor [IO.WatcherChangeTypes]::Renamed, 5000)
  Get-ChildItem -LiteralPath $inbox -File |
    Where-Object { $_.Name -notmatch '^\.|\.(part|crdownload|tmp)$' -and (Test-Ready $_.FullName) } |
    ForEach-Object {
      fcl convert $_.FullName --to $To --out-dir $out 2>> $log
      $target = if ($LASTEXITCODE -eq 0) { $done } else { $failed }
      Move-Item -LiteralPath $_.FullName -Destination $target -Force
    }
}

Save it as Watch-Convert.ps1 and run pwsh -File Watch-Convert.ps1 -Folder C:\Hotfolder -To pdf. Two design choices:

  • The folder is swept on every wake-up, not per event. FileSystemWatcher can drop events when many files arrive at once. Sweeping means a missed event only delays a file by 5 seconds instead of losing it, and files already sitting there at start get converted too
  • Test-Ready tries to open the file exclusively. On Windows that fails while another program still has the file open for writing, so half-copied files wait for the next pass

Use PowerShell 7 (pwsh). We ran the script with PowerShell 7.6. fcl exits with code 1 when a file fails, and that's what $LASTEXITCODE checks.

Run the Watch Folder as a Background Service

A watcher in a terminal window stops when you log out. Run it as a service instead.

Linux (systemd user service). Save this as ~/.config/systemd/user/fcl-watch.service:

[Unit]
Description=Convert scans dropped into ~/Scans to searchable PDF

[Service]
ExecStart=/usr/local/bin/fcl watch %h/Scans --to searchable-pdf --out-dir %h/Scans/searchable
Restart=on-failure
RestartSec=30

[Install]
WantedBy=default.target

Then systemctl --user enable --now fcl-watch and journalctl --user -u fcl-watch -f for the log. To use the script instead, point ExecStart at watch-convert.sh with its two arguments.

macOS (launchd). Save as ~/Library/LaunchAgents/com.example.fcl-watch.plist and load it with launchctl load ~/Library/LaunchAgents/com.example.fcl-watch.plist:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>Label</key><string>com.example.fcl-watch</string>
  <key>ProgramArguments</key>
  <array>
    <string>/usr/local/bin/fcl</string>
    <string>watch</string>
    <string>/Users/you/Scans</string>
    <string>--to</string>
    <string>searchable-pdf</string>
    <string>--out-dir</string>
    <string>/Users/you/Scans/searchable</string>
  </array>
  <key>RunAtLoad</key><true/>
  <key>KeepAlive</key><true/>
  <key>StandardErrorPath</key><string>/Users/you/Library/Logs/fcl-watch.log</string>
</dict>
</plist>

Windows (Task Scheduler). Register the PowerShell script to start at logon:

$action = New-ScheduledTaskAction -Execute 'pwsh.exe' -Argument '-NoProfile -WindowStyle Hidden -File C:\Scripts\Watch-Convert.ps1 -Folder C:\Hotfolder -To pdf'
Register-ScheduledTask -TaskName 'fcl watch folder' -Action $action -Trigger (New-ScheduledTaskTrigger -AtLogOn)

Run all three under your own user account, so fcl finds the session that fcl login stored in your config folder.

Troubleshooting a Watch Folder

  • Nothing happens when a file lands: with fcl watch, check it's in the top level of the folder and didn't exist before the watcher started. With inotifywait, check the file really arrives in inbox/ and not in a subfolder
  • A re-scanned file with the same name isn't converted: that's fcl watch converting each name once per session. Restart it, or use a script that moves files to done/
  • Rate limit exceeded: too many files in one minute for your plan. Retry after the wait time in the message, or sign in for a higher allowance
  • Daily limit exceeded: the day's credits are used up. Retrying won't help until tomorrow
  • Unsupported conversion: .xyz to pdf: that extension has no route to the target. It's logged, and the scripts move it to failed/
  • Half-converted or corrupt output: the file was picked up mid-copy. Raise the interval, or have the writer use a temporary name and rename at the end
  • Conversions run as guest after login: the service runs as a different user, or the stored session expired. Run fcl whoami as the service user and fcl login again if needed

For conversions you submit from your own code rather than a folder, the conversion API guide to jobs, polling and callbacks shows the same flow over HTTP.

Summary

For a folder that should auto-convert whatever lands in it, fcl watch <folder> --to <format> --out-dir <dir> is one command and needs no scripting. Once you need processed and failed subfolders, rate-limit retries or scanner files with repeated names, a short inotifywait, fswatch or FileSystemWatcher script around fcl convert does it. Keep the credits-per-minute limit in mind either way. Every flag is documented on the fcl CLI reference.

If you're converting a pile of existing files once rather than watching a folder, the batch file conversion guide compares the web queue, desktop apps and the CLI.

Related Tools

Frequently Asked Questions

How do I auto-convert files dropped into a folder?

Run fcl watch followed by the folder, --to with the target format and --out-dir with where results should go, for example fcl watch ~/inbox --to pdf --out-dir ~/converted. It checks the folder every few seconds and converts each new file until you press Ctrl+C.

Does fcl watch convert files that are already in the folder?

No. Files present when the watcher starts are left alone, and so are files that arrived while it was stopped. If you need those converted, use a watcher script that sweeps the folder at start, or run fcl convert on them once.

What happens if many files land in the watch folder at once?

Each conversion spends credits, and every plan has a per-minute cap. In our test a guest dropped 12 Word files at once: 10 converted and 2 failed with a rate-limit error. fcl watch doesn't retry them, so for bursts use a script that waits a minute and tries again, or sign in for a higher allowance.

Can a watch folder turn scans into searchable PDFs?

Yes. Watch the scanner's output folder with --to searchable-pdf, and add --ocr-language with a code such as de or fr if you know the document language. OCR costs 5 credits per file, so a guest can process 10 scans a day.

Does watch-folder conversion work on Windows and macOS?

Yes. fcl watch works the same on Linux, macOS and Windows. For moving processed files and retrying failures, use a small script: inotifywait on Linux, fswatch on macOS, or a PowerShell FileSystemWatcher loop on Windows.

Are the files converted on my computer?

No. fcl uploads each file over HTTPS to FileConvertLab's servers, where the conversion runs, then downloads the result into your output folder. How uploads are handled is described in the privacy policy.