Automatically match PayPal transactions with YNAB bank transactions to identify original merchants
This tool solves a common problem for YNAB users who make online purchases through PayPal: transactions appear in your bank account labeled only as "PayPal", making it difficult to track which retailer you actually paid. This matcher intelligently connects your YNAB transactions with PayPal transaction details using smart date and amount matching with tolerance for currency conversion and banking delays.
When you use PayPal for online purchases:
- Bank transactions appear labeled as "PayPal" without merchant details
- Transactions are imported into YNAB without the original retailer information
- You must manually cross-reference PayPal to identify each purchase
- Currency conversions make amount matching difficult
This tool:
- Fetches transactions from both YNAB and PayPal
- Intelligently matches them based on amount and date (with tolerance for delays and currency conversion)
- Displays matched merchant information with confidence scoring
- Optionally updates YNAB transactions with merchant details
- Flexible PayPal Integration: CSV exports (Personal accounts) or REST API (Business accounts)
- Smart Matching Algorithm:
- Multi-day clearing delay tolerance (configurable)
- Currency conversion fluctuation tolerance (±3% default)
- Confidence scoring (high/medium/low)
- Multiple Currencies: Handles GBP, EUR, USD, AUD, and more
- Safe Operation: Preview matches before applying updates to YNAB
- Beautiful CLI: Rich formatted output with tables, progress bars, and color coding
- Python 3.13 or higher
- uv (recommended) or pip
- YNAB account with Personal Access Token
- PayPal account (Personal or Business)
# Clone repository
git clone https://github.com/yourusername/ynab-pp-sync.git
cd ynab-pp-sync
# Run directly (uv handles dependencies automatically)
uv run main.py --help# Clone repository
git clone https://github.com/yourusername/ynab-pp-sync.git
cd ynab-pp-sync
# Install dependencies
pip install -r requirements.txt
# Run tool
python main.py --help- Set up configuration file
cp .env.example .env- Configure YNAB credentials
Get your Personal Access Token from YNAB Account Settings:
YNAB_API_TOKEN=your_token_hereFind your Budget ID in your YNAB URL (https://app.ynab.com/<BUDGET_ID>/budget):
YNAB_BUDGET_ID=your_budget_id_here- Configure PayPal source
Option A: CSV Export (Recommended for Personal accounts)
Export transactions from PayPal:
- Go to PayPal → Activity → Download
- Select: All transactions, date range, CSV format
- Save as
paypal_transactions.csv
PAYPAL_CSV_PATH=paypal_transactions.csvOption B: API (Business accounts only)
Create app at PayPal Developer Dashboard:
PAYPAL_CLIENT_ID=your_client_id_here
PAYPAL_CLIENT_SECRET=your_client_secret_here
PAYPAL_MODE=live- Customize PayPal CSV date format (optional)
Specify the date format used in your PayPal CSV export:
# For UK/Europe/Australia (DD/MM/YYYY)
PAYPAL_DATE_FORMAT=%d/%m/%Y
# For US (MM/DD/YYYY)
PAYPAL_DATE_FORMAT=%m/%d/%Y
# For ISO format (YYYY-MM-DD)
PAYPAL_DATE_FORMAT=%Y-%m-%d
# Or use auto-detection (default)
PAYPAL_DATE_FORMAT=auto- Configure YNAB transaction filtering (optional)
By default, the tool only matches unapproved transactions. This focuses on transactions that you haven't confirmed yet:
# Only match unapproved transactions (default: true)
YNAB_ONLY_UNAPPROVED=true
# Optionally filter by cleared status (default: false)
YNAB_ONLY_UNCLEARED=falseTo include all transactions regardless of approval status:
YNAB_ONLY_UNAPPROVED=false- Customize matching parameters (optional)
DATE_TOLERANCE_DAYS=7
AMOUNT_TOLERANCE_PERCENT=3.0
PAYPAL_KEYWORDS=PayPal,PAYPAL,Pp *uv run main.py testVerifies YNAB and PayPal API credentials.
uv run main.py [OPTIONS]Options:
--days INTEGER- Days to look back (default: 90)--use-api- Use PayPal API instead of CSV--csv PATH- Path to PayPal CSV file--output PATH- Save results to file--update- Update YNAB with matches--confidence [high|medium|low]- Minimum confidence for updates (default: high)--env PATH- Path to .env file (default: .env)--help- Show help message
View matches from last 30 days:
uv run main.py --days 30Use PayPal API (Business accounts):
uv run main.py --use-api --days 30Save results to file:
uv run main.py --days 30 --output matches.txtUpdate YNAB with high-confidence matches:
uv run main.py --days 30 --update --confidence highInclude medium-confidence matches:
uv run main.py --days 30 --update --confidence mediumMatch Statistics
┌────────────────────────────────────┬─────────┐
│ Total YNAB PayPal transactions: │ 45 │
│ Matched: │ 42 (93%)│
│ Unmatched: │ 3 │
└────────────────────────────────────┴─────────┘
HIGH CONFIDENCE MATCHES (38)
┏━━━━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━┳━━━━━━━┳━━━━━━┓
┃ YNAB Date ┃ Amount ┃ PayPal Date┃ Merchant ┃ Score ┃ Days ┃
┡━━━━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━╇━━━━━━━╇━━━━━━┩
│ 2025-11-08 │ £42.99 │ 2025-11-05 │ Amazon UK │ 0.95 │ 3 │
│ 2025-11-07 │ £15.50 │ 2025-11-06 │ Spotify (Premium) │ 0.92 │ 1 │
└────────────┴────────┴────────────┴───────────────────┴───────┴──────┘
The tool searches YNAB for transactions containing PayPal keywords (configurable in .env):
- Checks payee name and memo fields
- Only considers outgoing transactions (negative amounts)
- By default, filters to unapproved transactions only
Why filter by unapproved?
- Unapproved transactions are those you haven't confirmed yet - exactly the ones that need review
- Once you have the actual merchant name (e.g., "Amazon UK" instead of just "PayPal"), you can approve the transaction
- This reduces noise from already-approved transactions
- Improves match quality by focusing on transactions that need attention
- Can be disabled via
.envsettings if you want to match all transactions
For each YNAB PayPal transaction:
Date Matching:
- Looks for PayPal transactions 0-7 days BEFORE the YNAB date
- Accounts for bank clearing delays (weekends, holidays)
Amount Matching:
- Compares amounts with ±3% tolerance (default)
- Handles currency conversion fluctuations
- YNAB amounts in GBP; PayPal may be EUR, USD, AUD, etc.
Scoring:
- Date proximity (closer = better)
- Amount accuracy (closer = better)
- Currency bonus (GBP transactions get higher confidence)
Confidence Levels:
- High (score ≥ 0.9): Very likely correct - safe for auto-update
- Medium (score ≥ 0.7): Probably correct - review recommended
- Low (score ≥ 0.5): Uncertain - manual verification needed
Displays matches categorized by confidence level with detailed information.
When using --update:
- Updates memo field with merchant name and details
- Example: "Amazon UK (Wireless Mouse) [EUR 49.99]"
- Only updates transactions meeting confidence threshold
- Requires confirmation before proceeding
- Check
PAYPAL_KEYWORDSsetting in.env - Verify how PayPal transactions appear in YNAB
- Try adding variations: "PP*", "PAYPAL*"
- Ensure CSV is exported from PayPal
- Check
PAYPAL_CSV_PATHin.env - Verify file location
- Verify
YNAB_API_TOKENis correct - Check
YNAB_BUDGET_IDis correct - Ensure token hasn't expired
- Verify Transaction Search permission is enabled
- Wait up to 9 hours after enabling permission
- Check Client ID and Secret
- Verify
PAYPAL_MODEsetting (live/sandbox)
Adjust parameters in .env:
- Increase
DATE_TOLERANCE_DAYSif transactions take longer to clear - Increase
AMOUNT_TOLERANCE_PERCENTfor more currency variation - Check bank statement timing vs PayPal dates
- PayPal CSV formats vary by region
- Tool auto-detects column names and date formats
- Ensure "Comma Delimited" format was selected when exporting
- If dates aren't parsing correctly, set
PAYPAL_DATE_FORMATin.envto match your CSV format:%d/%m/%Yfor DD/MM/YYYY (UK, Europe, Australia)%m/%d/%Yfor MM/DD/YYYY (US)%Y-%m-%dfor YYYY-MM-DD (ISO)
Contributions welcome! Areas for improvement:
- Support for additional payment providers (Stripe, Revolut, etc.)
- Enhanced matching algorithms
- Better CSV format detection
- Web interface
- Additional output formats (JSON, Excel)
Please feel free to open an issue or submit a pull request.
MIT © 2025