Skip to main content

Python library for Swift MT and ISO 20022 message parsing and generation

Project description

swiftlib

A production-quality Python 3 library for parsing and generating SWIFT MT messages and ISO 20022 XML messages. Designed as a modern Python alternative to the Java prowide-core library.


Overview

swiftlib provides:

  • SWIFT MT parsing and generation — full support for MT103, MT202, MT202COV, MT515, MT700, MT900, MT910, MT940, MT942, MT950
  • ISO 20022 XML parsing and generation — pain.001, camt.053, pacs.008 (extensible to any message type)
  • MT ↔ ISO 20022 converters — MT103 ↔ pain.001, MT103 ↔ pacs.008, MT940 ↔ camt.053
  • Typed Python dataclasses — all message components are dataclasses with full type hints
  • Fluent builder pattern — intuitive API for constructing messages step-by-step

Why better than prowide-core?

Feature swiftlib (Python) prowide-core (Java)
Runtime No JVM required Requires JDK 8+
Install pip install swiftlib Maven/Gradle dependency
Types Python type hints + mypy Java generics
Data model Python dataclasses Java POJOs/builders
Serialisation Native json.dumps, no mapping required Requires Jackson/Gson
Decimal precision decimal.Decimal (exact) BigDecimal (verbose)
Date handling datetime.date objects java.time.LocalDate
Async compatible Yes — no shared state JVM threading model
IDE support Full autocomplete via type hints Depends on IDE plugin
Integration FastAPI, Django, Celery, asyncio Spring, Jakarta EE

Installation

pip install swiftlib

Requirements: Python 3.10+, lxml>=4.9, pydantic>=2.0


Quick Start

Parsing an MT103

from swiftlib.mt import parse

raw = """{1:F01BANKBEBBAXXX0000000000}{2:I103BANKDEFFXXXXU}{4:
:20:FT21099B59YFW001
:23B:CRED
:32A:210101USD10000,
:50K:/12345678
JOHN DOE
123 MAIN ST
:59:/98765432
JANE SMITH
:70:PAYMENT FOR SERVICES
:71A:OUR
-}"""

msg = parse(raw)  # Returns a typed MT103 instance
print(msg.message_type)   # "103"
print(msg.amount)          # Decimal("10000")
print(msg.currency)        # "USD"
print(msg.value_date)      # date(2021, 1, 1)
print(msg.charges)         # "OUR"

Building an MT103

from swiftlib.mt.messages.mt103 import MT103Builder
from decimal import Decimal
from datetime import date

mt103 = (
    MT103Builder()
    .transaction_reference("FT21099B59YFW001")
    .bank_operation_code("CRED")
    .value_date_currency_amount(date(2021, 1, 1), "USD", Decimal("10000.00"))
    .ordering_customer("/12345678", "JOHN DOE", ["123 MAIN ST", "NEW YORK NY"])
    .account_with_institution("A", "BANKDEFF")
    .beneficiary("/98765432", "JANE SMITH", ["456 OAK AVE", "BERLIN 10115"])
    .remittance_info("PAYMENT FOR SERVICES")
    .charges("OUR")
    .build()
)

print(mt103.to_swift())
print(mt103.to_json())

Parsing an ISO 20022 pain.001

from swiftlib.iso20022 import parse

with open("payment.xml") as f:
    msg = parse(f.read())

print(msg.group_header.message_id)
print(msg.payment_information[0].debtor.name)
print(msg.payment_information[0].credit_transfer_transactions[0].amount.amount)

Generating a pain.001

from swiftlib.iso20022.messages.pain001 import (
    Pain001Builder, Party, PaymentInformation,
    CreditTransferTransaction, PaymentIdentification,
    InstructedAmount, AccountIdentification,
    BranchAndFinancialInstitutionIdentification,
    FinancialInstitutionIdentification,
)
from datetime import date, datetime
from decimal import Decimal

fi = FinancialInstitutionIdentification(bic="BANKDEFF")
agt = BranchAndFinancialInstitutionIdentification(financial_institution=fi)

tx = CreditTransferTransaction(
    payment_id=PaymentIdentification(end_to_end_id="E2E-001"),
    amount=InstructedAmount(currency="EUR", amount=Decimal("5000.00")),
    charge_bearer="SLEV",
    creditor_agent=agt,
    creditor=Party(name="BENEFICIARY"),
    creditor_account=AccountIdentification(iban="DE98765432109876543210"),
)

pi = PaymentInformation(
    payment_info_id="PI-001",
    payment_method="TRF",
    batch_booking=False,
    number_of_transactions=1,
    control_sum=Decimal("5000.00"),
    payment_type_info=None,
    requested_execution_date=date(2021, 1, 1),
    debtor=Party(name="PAYER"),
    debtor_account=AccountIdentification(iban="US12345678901234567890"),
    debtor_agent=agt,
    credit_transfer_transactions=[tx],
)

pain001 = (
    Pain001Builder()
    .message_id("MSG-001")
    .creation_datetime(datetime.now())
    .initiating_party(Party(name="MY COMPANY"))
    .add_payment_information(pi)
    .build()
)

xml = pain001.to_xml()

Supported Message Types

SWIFT MT

Message Type Description Status
MT103 Single Customer Credit Transfer Full
MT202 General Financial Institution Transfer Full
MT202COV General FI Transfer (Cover) Full
MT515 Client Confirmation of Purchase/Sale Full
MT700 Issue of a Documentary Credit Full
MT900 Confirmation of Debit Full
MT910 Confirmation of Credit Full
MT940 Customer Statement Message Full
MT942 Interim Transaction Report Full
MT950 Statement Message Full

ISO 20022

Message Type Description Status
pain.001.001.09 Customer Credit Transfer Initiation Full
pain.001.001.03 Customer Credit Transfer Initiation (v3) Supported
camt.053.001.08 Bank to Customer Statement Full
camt.053.001.02 Bank to Customer Statement (v2) Supported
pacs.008.001.08 FI to FI Customer Credit Transfer Full
pacs.008.001.02 FI to FI Customer Credit Transfer (v2) Supported

API Reference Highlights

swiftlib.mt

from swiftlib.mt import parse, MT103, MT940, MT202

# Parse any MT message (returns typed instance)
msg = parse(raw_swift_string)

# Parse specifically as MT103
mt103 = MT103.from_swift(raw)

# Typed property access
mt103.transaction_reference  # str
mt103.value_date             # datetime.date
mt103.currency               # str
mt103.amount                 # Decimal
mt103.charges                # "OUR" | "SHA" | "BEN"
mt103.ordering_customer      # str (multiline)
mt103.beneficiary            # str (multiline)

# Validation
errors = mt103.validate()    # list[str]
mt103.is_valid()             # bool

# Serialisation
mt103.to_swift()             # SWIFT FIN string
mt103.to_dict()              # dict
mt103.to_json()              # JSON string

swiftlib.iso20022

from swiftlib.iso20022 import ISO20022Parser, ISO20022Generator

parser = ISO20022Parser()
msg = parser.parse(xml_string)          # auto-detect type
msg = parser.parse_file("payment.xml")  # from file

generator = ISO20022Generator()
xml = generator.generate(pain001)       # str
xml_bytes = generator.generate_bytes(pain001)  # bytes

swiftlib.converters

from swiftlib.converters import (
    MT103ToPain001Converter,
    MT103ToPacs008Converter,
    MT940ToCamt053Converter,
    Pain001ToMT103Converter,
    Pacs008ToMT103Converter,
)

# MT103 → pain.001
pain001 = MT103ToPain001Converter().convert(mt103)

# MT103 → pacs.008
pacs008 = MT103ToPacs008Converter().convert(mt103)

# pain.001 → list[MT103]
mt103_list = Pain001ToMT103Converter().convert(pain001)

# MT940 → camt.053
camt053 = MT940ToCamt053Converter().convert(mt940)

Conversion

from swiftlib.mt import MT103
from swiftlib.converters import MT103ToPain001Converter, Pain001ToMT103Converter

# Parse MT103
mt103 = MT103.from_swift(raw_mt103)

# Convert to pain.001
pain001 = MT103ToPain001Converter().convert(mt103)
xml = pain001.to_xml()

# Convert back to MT103
mt103_list = Pain001ToMT103Converter().convert(pain001)
restored = mt103_list[0]
print(restored.to_swift())

Integration Examples

FastAPI

from fastapi import FastAPI, HTTPException
from swiftlib.mt import parse
from swiftlib.exceptions import ParseError

app = FastAPI()

@app.post("/parse-mt")
async def parse_mt(body: str):
    try:
        msg = parse(body)
        return msg.to_dict()
    except ParseError as e:
        raise HTTPException(status_code=400, detail=str(e))

@app.post("/convert/mt103-to-xml")
async def convert_mt103_to_xml(body: str):
    from swiftlib.mt.messages.mt103 import MT103
    from swiftlib.converters import MT103ToPain001Converter
    mt103 = MT103.from_swift(body)
    pain001 = MT103ToPain001Converter().convert(mt103)
    return {"xml": pain001.to_xml()}

Django

# views.py
from django.http import JsonResponse, HttpRequest
from swiftlib.mt import parse

def parse_swift(request: HttpRequest) -> JsonResponse:
    raw = request.body.decode("utf-8")
    try:
        msg = parse(raw)
        return JsonResponse(msg.to_dict())
    except Exception as e:
        return JsonResponse({"error": str(e)}, status=400)

Celery

from celery import Celery
from swiftlib.mt import parse
from swiftlib.converters import MT103ToPain001Converter

app = Celery("tasks", broker="redis://localhost:6379/0")

@app.task
def process_mt103(raw_swift: str) -> str:
    """Process an MT103 and return pain.001 XML."""
    from swiftlib.mt.messages.mt103 import MT103
    mt103 = MT103.from_swift(raw_swift)
    pain001 = MT103ToPain001Converter().convert(mt103)
    return pain001.to_xml()

asyncio

import asyncio
from swiftlib.mt import parse

# swiftlib is thread-safe and stateless — safe to use in async code
async def process_messages(messages: list[str]) -> list[dict]:
    loop = asyncio.get_event_loop()
    results = []
    for raw in messages:
        # Run CPU-bound parsing in thread pool to avoid blocking event loop
        msg = await loop.run_in_executor(None, parse, raw)
        results.append(msg.to_dict())
    return results

Building from Source

git clone https://github.com/your-org/swiftlib.git
cd swiftlib
pip install -e ".[dev]"

Running Tests

# All tests
pytest

# With coverage
pytest --cov=swiftlib --cov-report=html

# Specific test file
pytest tests/mt/test_mt103.py -v

# Specific test
pytest tests/mt/test_parser.py::TestParseMT103Basic::test_parse_mt103_basic -v

Code Quality

# Lint
ruff check src/

# Type check
mypy src/swiftlib/

# Format
ruff format src/

Contributing

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/mt700-improvements)
  3. Add tests for your changes
  4. Ensure all tests pass (pytest)
  5. Ensure type checks pass (mypy src/swiftlib/)
  6. Submit a pull request

Adding a New MT Message Type

  1. Create src/swiftlib/mt/messages/mtXXX.py
  2. Subclass MTMessage, define _MESSAGE_TYPE, _MANDATORY_FIELDS, _FIELD_ORDER
  3. Add typed property accessors
  4. Implement a builder class
  5. Register in src/swiftlib/mt/messages/__init__.py (_MESSAGE_REGISTRY)
  6. Add tests in tests/mt/

Adding a New ISO 20022 Message Type

  1. Create a dataclass module in src/swiftlib/iso20022/messages/
  2. Add namespace to namespaces.py
  3. Add parser method in ISO20022Parser._dispatch()
  4. Add generator method in ISO20022Generator.generate()
  5. Add tests in tests/iso20022/

License

MIT License. See LICENSE for details.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

swiftlib-1.0.0.tar.gz (54.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

swiftlib-1.0.0-py3-none-any.whl (56.4 kB view details)

Uploaded Python 3

File details

Details for the file swiftlib-1.0.0.tar.gz.

File metadata

  • Download URL: swiftlib-1.0.0.tar.gz
  • Upload date:
  • Size: 54.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.4

File hashes

Hashes for swiftlib-1.0.0.tar.gz
Algorithm Hash digest
SHA256 baa95523355d7b979de33831908aacfde802eb27f19f47eb91b3d8445e2d249e
MD5 cf187eb90ec1af9d6ea9c9eeeffea470
BLAKE2b-256 da896db610e133f879f408d6c1f5a559dcd042b14a2be20430470e31b7bb2cc6

See more details on using hashes here.

File details

Details for the file swiftlib-1.0.0-py3-none-any.whl.

File metadata

  • Download URL: swiftlib-1.0.0-py3-none-any.whl
  • Upload date:
  • Size: 56.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.4

File hashes

Hashes for swiftlib-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 52c633e6df48e6cbf0f78445f8751c1d6da81f45336cc446f9c8c06b3080413c
MD5 43a3d5de206fbb867c19d51d2fba5aa5
BLAKE2b-256 c01a2e053330f911110ba021435dfcb2093275f7b163c496e515a8f96d511b5f

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page