Back to Solutions

Kody Pocztowe PL

A self-hosted PHP application that parses the official Polish Post postal code register into a searchable MySQL database — complete with an admin dashboard, a public search widget, a REST API, and usage statistics.

Kody Pocztowe PL

What is Kody Pocztowe PL?

Kody Pocztowe PL ("Polish Postal Codes") turns the official spispna.pdf register published by Polish Post into a structured, searchable database. It parses roughly 115,000 postal codes — covering cities, streets, house numbers, communities, counties, and voivodeships — and gives you an admin panel, a public-facing search widget, and a token-protected REST API to query that data from your own applications.

Key Advantage

One PDF In, a Full Lookup Service Out

Upload the official PNA register PDF (or let the app download it directly from Polish Post), run the parser, and you have a live, indexed MySQL database — plus a search page, an embeddable widget, and an API — without writing a single line of parsing code.

One-click installer, no Composer required
MySQL database or CSV export
Full UTF-8 support for Polish characters
Quarterly data updates from Polish Post

Feature Overview

PDF Parser
Reads all 4 parts of the official PNA register and loads them into MySQL or exports to CSV.
Search Engine
Look up records by postal code, city, or street — in the admin panel or on a public page.
Embeddable Widget
Drop an iframe on your own site so visitors can search postal codes directly.
Statistics & Charts
Daily query volume, top searches, API usage, and record counts by voivodeship.
REST API
Token-authenticated JSON API for looking up codes from external applications.
Secure by Default
Session login, CSRF protection, and PDO prepared statements throughout.

Installation

Kody Pocztowe PL ships with a one-click web installer — there is no Composer step and no manual SQL to run by hand.

Upload the files

Extract KodyPocztowePL.zip and upload the contents to any folder on your PHP 8.0+ hosting account via FTP, SFTP, or your control panel's file manager.

Run the installer

Open install.php in your browser. It checks PHP requirements, creates the MySQL database schema, and lets you set your admin login and password.

Delete install.php

Once setup is complete, remove install.php from the server (or use the one-click Delete install.php button shown in Configuration) to close the installer as a potential entry point.

Load the PDF register

Log in, go to PDF Parser, and either upload the official spispna.pdf file from disk or click Download from URL to fetch it directly from Polish Post.

Parse into the database

Select which of the 4 parts to load and click Start Parsing. Progress streams in real time — the full register is large, so parsing runs page-by-page over AJAX and can take several minutes.

Remove install.php after setup. The Configuration page shows a security warning banner and a one-click delete button for as long as install.php remains on the server — running it again could overwrite existing data.

Login & Security

Access to the admin panel is protected by a session-based login system, with every state-changing request guarded by a CSRF token and every database query built with PDO prepared statements.

Session-Based Login
The admin panel sits behind a login form. Credentials can be changed at any time from Configuration — the current password is always required to confirm the change.
CSRF Token Protection
Every form submission and AJAX write action carries a CSRF token, preventing cross-site request forgery against the admin panel.
PDO Prepared Statements
All database access goes through PHP PDO with bound parameters — there is no string-concatenated SQL anywhere in the request path, which rules out SQL injection by construction.
Installer Self-Removal
Configuration detects whether install.php is still present and offers a one-click button to delete it, closing off the installation flow once setup is finished.
Change the default credentials immediately after installation, and keep install.php deleted whenever the application is publicly reachable.

Dashboard & Database Status

The dashboard is the first screen after login. It shows database connection status, your license plan, the loaded PDF version, and a per-table breakdown of how many records are stored.

TableContent
Part 1Cities and streets
Part 2Institutions
Part 3Polish Post Offices
Part 4Polish Post Units

Each table row shows whether it is Filled or Empty, along with its record count, so you can see at a glance which parts of the register still need to be loaded.

Most lookups only need Part 1 (cities and streets). The other parts cover institutions and postal units and are optional depending on your use case.

PDF Parser

The parser reads the official spispna.pdf register page by page and writes the extracted records into MySQL (or a downloadable CSV file).

Loading the Source File

You can either upload a PDF from disk or let the app download it from a URL — by default the official Polish Post address, configurable in Configuration if it ever changes. After loading, the app automatically detects the file's version (month and year of release).

Parsing Options

OptionEffect
Keep existing dataSkip duplicates, add only new entries
Clear tables before loadingTruncate the table, then import fresh
Recreate tablesDROP + CREATE — rebuild the table structure from scratch

Parsing targets either the MySQL database or a CSV export, and you can choose which of the 4 parts to process in a single run. Progress is streamed live in the parsing log, with a Stop button to halt after the current page.

The source PDF is large (7 MB+). Parsing is intentionally done page-by-page over AJAX to avoid overloading the server — the full run can take several minutes, which is expected behavior.

Quarterly Updates

Polish Post publishes a new PNA register roughly every three months. To refresh your data: upload the new PDF, choose Clear tables before loading (or Keep if you only want to append new entries), and start parsing again.

Index Optimization

After a full import, use the Optimize Indexes button in Configuration. It runs ANALYZE + OPTIMIZE TABLE across all four tables, defragmenting the index tree and refreshing the query optimizer's statistics.



Statistics & Charts

The Statistics page gives a quick overview of how the search engine and API are being used, plus a breakdown of your loaded data.

Query Volume
Queries today, over the last 7 days, the last 30 days, and all-time totals, plus a daily activity chart for the last 30 days.
Top Searches
The top 10 most-searched queries over the last 30 days, broken down by search type and source (API, admin search, or public widget).
API Token Usage
Per-token call counts and last-used timestamps over the last 30 days, so you can see which integrations are active.
Records by Voivodeship
A breakdown of how many postal codes and records are loaded for each of Poland's voivodeships, with each region's share of the total.
Statistics are a Pro-plan feature, along with the public widget and the REST API.

REST API Documentation

The JSON REST API lets external applications look up postal codes programmatically. It requires a valid API token (Pro plan or higher) and responds over a single endpoint, api/index.php.

Authentication

Pass your token in one of two ways:

Authorization: Bearer YOUR_TOKEN_HERE
GET /api/index.php?action=lookup&token=YOUR_TOKEN_HERE&pna=00-001

The header method is recommended for production use since URL-embedded tokens can end up in server access logs. The URL parameter is convenient for quick tests or integrations (e.g. an embedded iframe) where setting custom headers isn't practical.

Lookup Endpoint

A single action=lookup endpoint accepts one of three mutually-exclusive search parameters:

ParameterDescriptionRequired
pnaPostal code, e.g. 00-001one of pna / city / street
cityCity nameone of pna / city / street
streetStreet nameone of pna / city / street
pagePage number (default 1)optional
per_pageResults per page, max 100 (default 10)optional
partsSearch all 4 PDF parts (1) or only part 1 (0, default)optional

The search term must be at least 2 characters long.

Response Format

Success (HTTP 200):

{
    "success": true,
    "data": {
        "total": 42,
        "page": 1,
        "per_page": 10,
        "results": [ { "pna": "00-001", "city": "Warszawa", "street": "..." } ]
    }
}

Error (HTTP 400 / 401 / 403 / 429):

{
    "success": false,
    "message": "Nieprawidłowy lub brak tokenu autoryzacyjnego."
}

HTTP Status Codes

CodeMeaning
200Success
400Missing required parameter or query too short
401Missing or invalid authorization token
403Feature requires the Pro plan, or the token's IP whitelist blocked the request
429Rate limit exceeded for this token
The API includes CORS headers (Access-Control-Allow-Origin: *) so it can be called directly from browser-based front-ends, not just server-to-server.

Managing API Tokens

Every API call is authenticated with a token created and managed from the API Tokens page in the admin panel.

Create a Token
Give it a descriptive name (e.g. "Homepage" or "Mobile App"). The full token key is shown once at creation time — save it immediately, as it cannot be displayed again afterward.
Rate Limits
Set an hourly and/or daily request limit per token. A value of 0 means unlimited.
IP Whitelist
Restrict a token to specific IP addresses or wildcard patterns, e.g. 192.168.1.* or 10.0.*.*. Leave the list empty for no IP restriction.
Expiration
Tokens can be set to expire on a given date, or left unlimited (∞). Expired tokens are rejected by the API automatically.
Per-Token Usage Stats
Each token has its own usage page: calls today/7 days/30 days/total, an activity chart, and a log of recent queries.
PlanToken limit
BasicAPI not available
ProUp to 3 tokens
DeveloperUnlimited tokens

System Requirements

Kody Pocztowe PL is a self-hosted application — install it on any standard PHP/MySQL hosting account.

ParameterValue
PHP versionPHP 8.0+
DatabaseMySQL 5.7+ / MariaDB
Character encodingUTF-8 throughout
DependenciesNone — no Composer, single self-contained folder
Admin UITabler.io, one-click installer
Coding standardPSR compliant
Data sourcespispna.pdf — official quarterly Polish Post release
Everything lives in a single folder that can be uploaded to any subfolder of your hosting account — no absolute paths, no files required outside the web root.

License Comparison

Kody Pocztowe PL is licensed per domain, with three tiers that unlock progressively more of the application.

Feature Basic Pro Developer
Domains15Unlimited
spispna.pdf parser (4 tables)
PDF download from URL + version detection
MySQL database + CSV export
Dashboard + database status
Search in the admin panel
Public search + iframe widget—
Statistics + charts—
REST API + CORS + documentation—
API tokens—up to 3Unlimited
Lifetime updates
SupportCommunityEmailPriority
Enter your license key in Configuration → License after purchase to unlock the Pro or Developer plan on your domain.

Frequently Asked Questions

Common questions about deploying and using Kody Pocztowe PL.

How often do I need to update the postal code data?

Polish Post publishes a new spispna.pdf register about every three months. Re-run the parser with the new file whenever a new release is available — the app can detect the loaded PDF's version automatically.

Do I need Composer or any external libraries?

No. The application has zero Composer dependencies and ships as a single self-contained folder — upload it, run the installer, and you're done.

Can I embed the search widget on my own website?

Yes, on the Pro plan or higher. Enable public search in Configuration and copy the generated <iframe> embed code onto your site.

What happens if I leave install.php on the server?

The Configuration page will keep showing a security warning until it's deleted, since a reachable installer could be used to overwrite your existing data. Delete it (or use the in-app button) right after setup.

Can I run this on multiple domains?

Yes — licensing is per domain. The Basic plan covers 1 domain, Pro covers 5, and Developer is unlimited.

Is the REST API rate-limited?

Yes. Each API token can have its own hourly and daily request limits, plus an optional IP whitelist, configured from the API Tokens page.