Postcode API

German addresses

This API has been deprecated in favor of the International Address API. We will continue to support this country specific API, but we encourage using the International Address API for new implementations, as it is easier to implement and provides more functionality.

Complete postal area
Get a list of postal areas matching the input (postcode or city name).
Complete street
Given a postal area, get a list of streets matching the specified street name.
Complete house number
Retrieve a list of house numbers matching the specified house number.

This example demonstrates a form with autocompletion for a German address. It calls the completePostalArea, completeStreet and completeHouseNumber methods.

Reset form

Try…

  • Berlin, Bismarckplatz 1
  • 78266, Dörflinger Str. 2
  • Hamburg, Sachsenweg 8g
  • Pieschen-Nord/Trachenberge, Bremer Str. 5
  • Königswalde, Pöhlbergstr. 6

Please note that this is a demo based on an alternative data set, which is not suitable for use in a production setting or unit tests. In this data set the names of some streets have been shortened. For example, 'Dorfstr.' may be returned as 'Dorfs… | demo'.

Autocompleted JSON address data:


	

Code

The following example code can be used as a starting point for your implementation.

HTML

<form>
	<input type="hidden" class="input-postcode" />
	<input type="hidden" class="input-city-name" />

	<dl>
		<dt>
			<label for="postal-area">Postcode, city or district name</label>
		</dt>
		<dd>
			<input class="input-postal-area" id="postal-area" type="text" placeholder="14193 or Berlin" />
		</dd>
		<dt>
			<label for="street-name">Street</label>
		</dt>
		<dd>
			<input class="input-street-name" id="street-name" type="text" placeholder="Dorfplatz" />
		</dd>
		<dt>
			<label for="house-number">House number</label>
		</dt>
		<dd>
			<input class="input-house-number" id="house-number" type="text" placeholder="3" />
		</dd>
	</dl>

	<address class="autocomplete-result"></address>
</form>

Javascript

This example script is based on jQuery and jQuery UI Autocomplete.

Please make sure to change the demo resource URL's (see code comments).

(function () {
	'use strict';

	var $ = jQuery,
		// Select what type of house number validation is required, allowed values: strict, number, none
		completeHouseNumberValidationParameter = 'none',

		address = {
			'postcode': null,
			'cityId': null,
			'cityName': null,
			'streetId': null,
			'streetName': null,
			'houseNumber': null,
			'houseNumberStatus': null
		},
		postcodeFilter = null,

		// Div containing the autocomplete demo elements
		container = $('.demo-autocomplete-de'),

		// Change the following selectors to match your form elements.
		postcodeInput = container.find('.input-postcode'),
		cityNameInput = container.find('.input-city-name'),
		postalAreaInput = container.find('.input-postal-area'),
		streetNameInput = container.find('.input-street-name'),
		houseNumberInput = container.find('.input-house-number'),
		addressElement = container.find('.autocomplete-result'),

		// Development related elements, should not be used for your form.
		resetFormInput = container.find('.demo-autocomplete-reset'),
		addressDataElement = container.find('.autocomplete-data'),

		// Change the following URL's to your production URL's.
		// Do not use the demo URL's in your production environment or unit tests.
		postalAreaUrl = React.endpoints.Website.url + '/demo/de/autocomplete/postal-area/',
		streetNameUrl = React.endpoints.Website.url + '/demo/de/autocomplete/street/',
		houseNumberUrl = React.endpoints.Website.url + '/demo/de/autocomplete/house-number/';

	$(function () // DOMReady handler.
		{
			if (postalAreaInput.length === 0)
			{
				return;
			}

			autoCompleteAddressParts();
		}
	);

	$.widget('custom.completePostalArea', $.ui.autocomplete, {
		_renderItem: function (ul, item) {
			var div = $('<div>', {'text': item.cityName, 'class': 'ui-menu-item-wrapper complete-postal-area'});

			if (item.matchedName && item.matchedName !== item.cityName)
			{
				div.append($('<span>', {'text': item.matchedName}));
			}
			else if (item.cityAddition)
			{
				div.append($('<span>', {'text': item.cityAddition}));
			}

			return $('<li>').append(div).appendTo(ul);
		}
	});

	var autoCompleteAddressParts = function ()
	{
		postalAreaInput.completePostalArea({
			autoFocus: true,
			select: updatePostalArea,
			change: updatePostalArea,
			source: function (request, responseCallback)
			{
				var sourceScope = this;

				$.getJSON(
					postalAreaUrl + encodeURIComponent(request.term),
					'',
					function (items, status, xhr)
					{
						if (typeof items.exception !== 'undefined')
						{
							return console.error(items.exception);
						}

						// Format matches.
						for (var i = 0, item; item = items[i]; i++)
						{
							items[i].value = items[i].label = item.cityName;

							if (typeof item.postcode === 'number')
							{
								items[i].value = items[i].label = item.postcode + ' ' + item.cityName;
							}

							if (typeof item.matchedName === 'string')
							{
								items[i].label += ' (' + item.matchedName + ')';
							}
						}

						responseCallback(items);
					}
				).fail(function (jqXHR)
					{
						if (jqXHR.status !== 200)
						{
							// Server might have (capacity) issues, stop sending requests to be safe.
							sourceScope.close();
							sourceScope.disable();
						}
					}
				);
			}
		}).focus(
			function ()
			{
				if (address.cityId === null && postalAreaInput.val() !== '')
				{
					postalAreaInput.completePostalArea('search');
				}
			}
		);

		$.widget('custom.completeStreet', $.ui.autocomplete, {
			_renderItem: function (ul, item) {
				var div = $('<div>', {'text': item.streetName, 'class': 'ui-menu-item-wrapper complete-street'});

				if (postcodeFilter === null)
				{
					div.append($('<span>', {'text': item.postcode}));
				}

				return $('<li>').append(div).appendTo(ul);
			}
		});

		streetNameInput.completeStreet({
			autoFocus: true,
			select: updateStreetName,
			change: updateStreetName,
			source: function (request, responseCallback)
			{
				var sourceScope = this;

				if (address.cityId === null)
				{
					return; // Can't continue without cityId value.
				}

				$.getJSON(
					streetNameUrl + address.cityId + '/' + (postcodeFilter || '') + '/' + encodeURIComponent(request.term),
					'',
					function (items, status, xhr)
					{
						if (typeof items.exception !== 'undefined')
						{
							return console.error(items.exception);
						}

						// Format matches.
						for (var i = 0, item; item = items[i]; i++)
						{
							items[i].value = items[i].label = item.streetName;
						}

						responseCallback(items);
					}
				).fail(function (jqXHR)
					{
						if (jqXHR.status === 503 || jqXHR === 500)
						{
							// Server might have (capacity) issues, stop sending requests to be safe.
							sourceScope.close();
							sourceScope.disable();
						}
					}
				);
			}
		}).focus(
			function ()
			{
				if (address.cityId === null && postalAreaInput.val() !== '')
				{
					postalAreaInput.focus();
				}
				else if (address.streetId === null && streetNameInput.val() !== '')
				{
					streetNameInput.completeStreet('search');
				}
			}
		);

		$.widget('custom.completeHouseNumber', $.ui.autocomplete, {
			_renderItem: function (ul, item) {
				var div = $('<div>', {'text': item.houseNumber, 'class': 'ui-menu-item-wrapper complete-house-number'});

				if (item.postcode !== address.postcode)
				{
					// Display postcode if it's not the postcode selected during street completion
					div.append($('<span>', {'text': item.postcode}));
				}

				if (item.status === 'incomplete')
				{
					div.append($('<span>', {'text': 'select bus number…', 'class' : 'remark'}));
				}
				else if (item.status === 'unknown')
				{
					div.append($('<span>', {'text': '(unknown house number)', 'class' : 'remark'}));
				}

				return $('<li>').append(div).appendTo(ul);
			}
		});

		houseNumberInput.completeHouseNumber({
			autoFocus: true,
			select: function (event, ui)
			{
				if (ui.item.status === 'incomplete')
				{
					// Trigger a new search to complete missing house number parts.
					window.setTimeout(function () { houseNumberInput.completeHouseNumber('search', ui.item.houseNumber); }, 200);
				}

				updateHouseNumber(event, ui);
			},
			change: updateHouseNumber,
			source: function (request, responseCallback)
			{
				var sourceScope = this;

				if (address.streetId === null || address.postcode === null)
				{
					return;
				}

				$.getJSON(
					houseNumberUrl + address.cityId +
					'/' + address.streetId +
					'/' + address.postcode +
					'/' + encodeURIComponent(completeHouseNumberValidationParameter) +
					'/' + encodeURIComponent(request.term),
					'',
					function (items, status, xhr)
					{
						if (typeof items.exception !== 'undefined')
						{
							return console.error(items.exception);
						}

						// Format matches.
						for (var i = 0, item; item = items[i]; i++)
						{
							items[i].label = items[i].value = item.houseNumber;
						}

						responseCallback(items);
					}
				).fail(function (jqXHR)
					{
						if (jqXHR.status !== 200)
						{
							// Server might have (capacity) issues, stop sending requests to be safe.
							sourceScope.close();
							sourceScope.disable();
						}
					}
				);
			}
		}).focus(
			function ()
			{
				if (address.streetId === null && streetNameInput.val() !== '')
				{
					streetNameInput.focus();
				}
				else if (address.houseNumber === null && houseNumberInput.val() !== '')
				{
					houseNumberInput.completeHouseNumber('search');
				}
			}
		);

		resetFormInput.click(
			function ()
			{
				// Reset the street and house number parts, the rest will be reset by updatePostalArea
				address.houseNumber = null;
				address.houseNumberStatus = null;
				address.streetName = null;
				address.streetId = null;

				postalAreaInput.removeClass('input-error').val('').focus();
				streetNameInput.removeClass('input-error').val('');
				houseNumberInput.removeClass('input-error').val('');
				updatePostalArea();
			}
		);

		renderAddressData();
	};

	/**
	 * Update input element validity.
	 *
	 * @param {Element} inputElement AutoComplete input element to update validity for.
	 * @param {Boolean} isValid If true mark "input-valid", if false mark "input-error" if input is not empty.
	 */
	var updateValidity = function (inputElement, isValid)
	{
		if (isValid)
		{
			inputElement.removeClass('input-error').addClass('input-valid');
		}
		else
		{
			inputElement.removeClass('input-valid');
			inputElement.toggleClass('input-error', inputElement.val() !== '');
		}
	};

	/**
	 * Update PostalArea form and internal address state using selected autocomplete item.
	 *
	 * @param {Object} event Event that triggered update.
	 * @param {Object|null} ui Newly selected ui item, or null if no item was selected.
	 */
	var updatePostalArea = function (event, ui)
	{
		var item = ui ? ui.item : null;

		if (item === null)
		{
			address.cityName = null;
			address.cityId = null;
			address.postcode = null;

			updateInputValues(true);
			updateValidity(postalAreaInput, false);
			updateStreetName(event, null);
		}
		else if (
			address.cityName !== item.cityName
			|| address.cityId !== item.cityId
			|| postcodeFilter !== (item.postcode || null)
		)
		{
			address.cityName = item.cityName;
			address.cityId = item.cityId;
			address.postcode = item.postcode || null;

			updateInputValues(true);
			updateValidity(postalAreaInput, true);
			updateStreetName(event, null);
		}
	};

	/**
	 * Update StreetName form and internal address state using selected autocomplete item.
	 *
	 * @param {Object} event Event that triggered update.
	 * @param {Object|null} ui Newly selected ui item, or null if no item was selected.
	 */
	var updateStreetName = function (event, ui)
	{
		var item = ui ? ui.item : null;

		if (item === null)
		{
			address.streetName = null;
			address.streetId = null;

			updateValidity(streetNameInput, false);
			updateHouseNumber(event, null);
		}
		else if (
			address.cityName !== item.cityName
			|| address.postcode !== item.postcode
			|| address.streetName !== item.streetName
			|| address.streetId !== item.streetId
		)
		{
			address.cityName = item.cityName;
			address.postcode = item.postcode;
			address.streetName = item.streetName;
			address.streetId = item.streetId;

			updateInputValues();
			updateValidity(streetNameInput, true);
			updateHouseNumber(event, null);
		}
	};

	/**
	 * Update HouseNumber form and internal address state using selected autocomplete item.
	 *
	 * @param {Object} event Event that triggered update.
	 * @param {Object|null} ui Newly selected ui item, or null if no item was selected.
	 */
	var updateHouseNumber = function (event, ui)
	{
		var item = ui ? ui.item : null;

		if (item === null)
		{
			address.houseNumber = null;
			address.houseNumberStatus = null;

			updateValidity(houseNumberInput, false);
		}
		else
		{
			address.postcode = item.postcode;
			address.houseNumber = item.houseNumber;
			address.houseNumberStatus = item.status;

			updateInputValues();
			updateValidity(houseNumberInput, true);
		}

		renderAddress();
		renderAddressData();
	};

	/**
	 * Update form input values with validated address information, keep unvalidated autocomplete input fields as-is.
	 *
	 * @param {boolean} updatePostcodeFilter Always update postalArea postcode filter if true. Defaults to false.
	 */
	var updateInputValues = function (updatePostcodeFilter)
	{
		if (updatePostcodeFilter || postcodeFilter !== null)
		{
			postcodeFilter = address.postcode;
		}

		if (postcodeFilter !== null && address.cityName !== null)
		{
			postalAreaInput.val(postcodeFilter + ' ' + address.cityName);
		}
		else if (address.cityName !== null)
		{
			postalAreaInput.val(address.cityName);
		}

		if (address.streetName !== null)
		{
			streetNameInput.val(address.streetName);
		}

		if (address.houseNumber !== null)
		{
			houseNumberInput.val(address.houseNumber);
		}

		cityNameInput.val(address.cityName || '');
		postcodeInput.val(address.postcode || '');
	};

	var renderAddress = function ()
	{
		addressElement.empty();

		if (address.postcode === null || address.cityName === null || address.streetName === null)
		{
			return;
		}

		addressElement.append(address.streetName);

		if (address.houseNumber !== '')
		{
			addressElement.append(' ', address.houseNumber);
		}

		addressElement.append($('<br>'), address.postcode, ' ', address.cityName);
	};

	var renderAddressData = function ()
	{
		addressDataElement.text(JSON.stringify(address, null, 4));
		hljs.highlightBlock(addressDataElement.get(0));
	}
})();

CSS

The jQuery UI Autocomplete widget requires some functional CSS to work. Use one of jQuery UI's themes or create your own.

.ui-helper-hidden-accessible {
	border: 0;
	clip: rect(0 0 0 0);
	height: 1px;
	margin: -1px;
	overflow: hidden;
	padding: 0;
	position: absolute;
	width: 1px;
}

.ui-widget {
	box-shadow: 0 10px 15px rgba(0, 0, 0, .15);
}

.ui-widget-content {
	background-color: #fff;
}

.ui-state-active {
	background-color: #f0f6fa;
}

.ui-menu {
	padding: 0;
	z-index: 99;
}

.ui-menu-item {
	cursor: pointer;
}

.ui-menu-item-wrapper {
	padding: 8px 12px;
	line-height: 1;
}

.ui-autocomplete {
	position: absolute;
	max-height: 300px;
	overflow-y: auto;
 	overflow-x: hidden;
}