Private APIs
The secret API definitions that evil corp doesn't want you to know about.
Papa John's
Placing an order
TBD
Tracking an order
After placing an order through the Papa John's website, you will be redirected to an order confirmation page. An example URL for the order confirmation page is https://www.papajohns.co.uk/order-confirmation.aspx?OrderID=50009189&StoreID=603
.
Loading this page requires a set of request headers in order to load. These are listed below.
Show headers
GET /order-confirmation.aspx?OrderID=50009189&StoreID=603 HTTP/2
Host: www.papajohns.co.uk
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:88.0) Gecko/20100101 Firefox/88.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: en-GB,en;q=0.5
Accept-Encoding: gzip, deflate, br
Referer: https://www.papajohns.co.uk/stores/gloucester-hucclecote/checkout.aspx
Upgrade-Insecure-Requests: 1
Connection: keep-alive
Cookie: apay-session-set=Bl3nCyAOX%2FBVfTmEkUfBgoSmTcEw%2FJUs9LxmbtoyiOMR7BAh0AMIODSgZePNAmQ%3D; DismissCookieBanner=Yes; asuid=10780892; asuidHash=873B200E6B6D0A562D310F1B0092F0017845E09A; ABTasty=uid=dp5mvcj0p5p9jra5&fst=1616532916411&pst=1617991107615&cst=1621278139332&ns=3&pvt=83&pvis=15&th=509902.638027.6.6.1.1.1616533120187.1621278479723.1_642258.0.83.15.3.1.1616532918396.1621279878514.1_669083.829517.83.15.3.1.1616532916421.1621279877178.1_683095.847193.10.10.1.1.1616532916424.1621278423046.1_686078.-1.3.3.1.1.1621278151995.1621278423048.1; _gcl_au=1.1.1851597768.1616532918; _tq_id.TV-81185418-1.0dc4=2a7edf482385ca8b.1616532919.0.1621279879..; _ga=GA1.3.1237940931.1616532919; _hjid=112f0ff1-0101-4755-9d3b-ea21e193f987; _scid=09fe78da-41b7-4003-8c2f-6889bdf84a61; _fbp=fb.2.1616532920373.1093171843; user_transaction_ids=48334663,48881284,50009189; ak_bmsc=AFBDE736E8663562FD3ED51F00076DF05C7A36301C6F0000BABDA260FF513970~plafu8kPFJ6G/cFEHUsNPVBhEyLhaaOVZVaK76PrO/y5uHmShjLSedgFWe9U7eMy9gnHH6kZAS/ZYPcxI26vUqNlXvZy7RKOeUyYW7uD/tQraWH5+EuYSZH4xaujGtMvNmLlfpAAD+KwrFSsxaDhN/364f2SPHTV4pti4uZ8LEzkWeArAsIFv3X7dSd0/eDZQlr+LEU92GIGJys86toAFj8986fJfDIe6ZjrERK3dAWbvb4CKsW5imAIBkqE+LZztq; akavpau_www_papajohns_co_uk=1621280182~id=6cb323d81a232e42d7039d2701b5ddc9; ASP.NET_SessionId=wjhyilcrs42fd1vnt30zputb; AKA_A2=A; ABTastySession=mrasn=&lp=https%253A%252F%252Fwww.papajohns.co.uk%252F&sen=51; redwp=_; _gid=GA1.3.1029724204.1621278142; _hjIncludedInSessionSample=1; _hjTLDTest=1; RT="dm=papajohns.co.uk&si=06vr58e10r06&ss=1621278138296&sl=18&tt=37690&obo=0&sh=1621279878416%3D18%3A0%3A37690%2C1621278655905%3D17%3A0%3A35554%2C1621278554228%3D16%3A0%3A34251%2C1621278540025%3D15%3A0%3A33661%2C1621278525560%3D14%3A0%3A32576&rl=1&ld=1621279878416&r=https%3A%2F%2Fwww.papajohns.co.uk%2Forder-confirmation.aspx%3Fa9cff004ae9f6c0b5e7ea854acce19d7&ul=1621280290417"; bm_sv=E3B35A190C079033739A55312C0F6295~fjerq20nn9hVdRaqkx3GM97GwgamrOYzPqBSYjVEEMQBRxNnEVF+kK43KOAXD7pNMBlACbzsJR1ALQrCoX6QdoweXoNwn/3q9X1xEJt/KVXvH74DtyizJm6M1kV1B8tjbWeNv2Tb9O4HoJ6Ltf00WniXXThER58ZIbDEdWfuRYg=; SearchedPostcode=GL4 4DX; DelCol=Del; sid=603; language=en_GB; amazon-pay-connectedAuth=connectedAuth_general; oTrack=,50009189,; SL_C_23361dd035530_VID=qcHRHhZ4fMA; SL_C_23361dd035530_KEY=a1db90e2e7c3721df05d3bb9da4822e8acabe541; SL_C_23361dd035530_SID=Pv8s1UyuAx; _uetsid=687394d0b74211ebbd174720482c0e5a; _uetvid=6873c7f0b74211eb9221096978a1f758
Cache-Control: max-age=0
At the top of the page is a "LAUNCH TRACKER" banner/button, whose link contains a UUID. The URL can safely be extracted using .orderTrackerLink a
as a query selector.
This page looks a little something like this:
You can use the UUID extracted from this URL to query Papa John's backend. The backend URL is https://papajohns-ordertracker-live.azurewebsites.net/api/ordertracker/<UUID>
. GET requests to this URL are unauthenticated and return a JSON response that looks a little like this:
{
"id": "7f22ab7c-7793-4505-ac20-6df7889a5ee8",
"orderId": 50009189,
"orderTime": "20:08",
"orderType": 2,
"paymentType": "PayPal",
"storeName": "Gloucester - Hucclecote",
"storePhone": "01452 616165",
"statusId": 25,
"tipJarUrl": "",
"lastUpdated": "20:09",
"errorMessage": null
}
The statusId
field represents the status of the order. There are five status IDs in total. It is unclear why these specific IDs have been chosen, and I'm not certain that they'll remain this way forever... The ones I found are enumerated in the table below:
Status ID | Description |
---|---|
25 |
Order accepted |
5 |
In the oven |
20 |
Pizza peer review (-: |
6 |
On the road |
8 |
Supposedly delivered |
KFC
API key
Requests to the KFC API requires a KFC API key. This, in addition to several other API keys and endpoints, can be retrieved from the <script>
tag at the end of the KFC homepage. Requests to the KFC homepage must come from a legitimate browser (determined by the user agent).
An example is shown below (formatted with whitespace for readability - the original is minified).
$ curl 'https://www.kfc.co.uk/ \
-H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:109.0) Gecko/20100101 Firefox/111.0'
<script>
...
"orderOriginAnnotations": {
"fingerprintEndpointUrl": "https://fp.platform.kfcapi.com",
"fingerprintApiKey": "E8uHaPCQYVV9mgTDF2pB",
"fingerprintRegion": "eu"
}
...
"apiEndpoints": {
"aggregatorv3": "/aggregators?city={city}\u0026lat={latitude}\u0026lng={longitude}\u0026postcode={postcode}"
}
...
"firebase": {
"apiKey": "AIzaSyBfxHNPT4ZXUsZ53PYKRrJ3ihji6V4LgP4",
"authDomain": "kfc-uki.firebaseapp.com",
"databaseURL": "https://kfc-uki.firebaseio.com",
"projectId": "kfc-uki",
"storageBucket": "kfc-uki.appspot.com",
"messagingSenderId": "370388072022",
"appId": "1:370388072022:web:21b082985df244f42cedf7",
"measurementId": "G-2ZFPMGWCJ5"
}
...
"braze": {
"sdkEndpoint": "sdk.fra-01.braze.eu",
"sdkKey": "5ad9b4c0-4479-4fdd-a85a-509f317506a3"
}
...
"upsellsURL": "https://s3.eu-west-1.amazonaws.com/qa-kiosk.kfc.co.uk/upsells/prompts.json"
...
"runtimeConfig": {
"APP_TYPE": "website",
"NEXT_API": "https://www.kfc.co.uk",
"CMS_API": "https://uk.kfc-cms.com",
"KFC_API": "https://prod.kfcapi.com/api/v1",
"KFC_API_V3": "https://prod.kfcapi.com/api/v3",
"KFC_API_OVERRIDES": "{}",
"API_KEY": "siYAzKattmaIHSwMV9OJYtaoP8SRq",
"APP_ENV_CODE": "UK-GB",
"DELIVERY_GRAPHQL_API": "https://delivery.platform.kfcapi.com/graphql",
"DELIVERY_GRAPHQL_API_KEY": "da2-g4p57iyxirctrfv7inygfmdtuq",
"COUNTRY_CODE": "GB",
"CODE_MARKET": "UK",
"LANGCODE": "en",
"GOOGLE_API_KEY": "AIzaSyApKSHDYIv9udfHvZB34WB6sAGZYojr97E",
"RECAPTCHA_KEY": "6LcQBdoaAAAAAF8W2ucXcvQfO3wGQtrvpw2aVZRK",
"ROBOT_TXT": "https://brand-uk.assets.kfc.co.uk/robots_GB.txt",
"SITEMAP_URL": "https://brand-uk.assets.kfc.co.uk/sitemap_GB.xml",
"ENVIRONMENT": "production",
"KFC_SITES": "{\"GB\":\"https://www.kfc.co.uk\",\"IE\":\"https://www.kfc.ie\"}",
"BUILD_NUMBER": "e6949418d76f97f221b04c997f433c81d7dbde8a",
"ONETRUST_ID": "90b34549-608d-410c-ab2b-262c91ce2ae5",
"DATADOG_APPLICATION_ID": "e6eccdc7-5923-492c-a098-06fb694f25f6",
"DATADOG_CLIENT_ID": "pub898e007f540bf332315d09b8196beac6",
"MPARTICLE_API_KEY": "us1-5867eed1788b2d4ca14fd7301f0629f5"
}, "isFallback": false, "customServer": true, "gip": true, "scriptLoader": []
}
</script>
Testing across a period of 15 minutes, multiple user agents, with no cache/cookies, reveals that this API key is fairly 'static'.
A regular expression to extract the API key from this page is shown below:
(?<="API_KEY":\s\")[a-zA-Z0-9]{29}(?=\")
KFC locations
The following API endpoint returns KFC locations based on a lookup string.
https://prod.kfcapi.com/api/v1/util/google/lookup?address=<LOCATION>&geotype=Google
Example
$ curl --compressed 'https://prod.kfcapi.com/api/v1/util/google/lookup?address=Gloucester&geotype=Google' \
-H 'codemarket: UK' \
-H 'countrycode: GB' \
-H 'x-api-key: siYAzKattmaIHSwMV9OJYtaoP8SRq'
[
{
"description": "Gloucester, UK",
"matched_substrings": [
{
"length": 10,
"offset": 0
}
],
"place_id": "ChIJz_Lr9vv9cEgRzxnoIZabcOw",
"reference": "ChIJz_Lr9vv9cEgRzxnoIZabcOw",
"structured_formatting": {
"main_text": "Gloucester",
"main_text_matched_substrings": [
{
"length": 10,
"offset": 0
}
],
"secondary_text": "UK"
},
"terms": [
{
"offset": 0,
"value": "Gloucester"
},
{
"offset": 12,
"value": "UK"
}
],
"types": [
"locality",
"political",
"geocode"
]
},
{
"description": "Gloucestershire, UK",
"matched_substrings": [
{
"length": 10,
"offset": 0
}
],
"place_id": "ChIJvWBKlTVTcEgRYidBWWOI7Dc",
"reference": "ChIJvWBKlTVTcEgRYidBWWOI7Dc",
"structured_formatting": {
"main_text": "Gloucestershire",
"main_text_matched_substrings": [
{
"length": 10,
"offset": 0
}
],
"secondary_text": "UK"
},
"terms": [
{
"offset": 0,
"value": "Gloucestershire"
},
{
"offset": 17,
"value": "UK"
}
],
"types": [
"administrative_area_level_2",
"political",
"geocode"
]
},
{
"description": "Gloucester Road, Kensington, London, UK",
"matched_substrings": [
{
"length": 10,
"offset": 0
}
],
"place_id": "EidHbG91Y2VzdGVyIFJvYWQsIEtlbnNpbmd0b24sIExvbmRvbiwgVUsiLiosChQKEgnzHEc5XQV2SBHWVE8x6AIEjBIUChIJNxJ0UPQPdkgRXik3cFTbw-0",
"reference": "EidHbG91Y2VzdGVyIFJvYWQsIEtlbnNpbmd0b24sIExvbmRvbiwgVUsiLiosChQKEgnzHEc5XQV2SBHWVE8x6AIEjBIUChIJNxJ0UPQPdkgRXik3cFTbw-0",
"structured_formatting": {
"main_text": "Gloucester Road",
"main_text_matched_substrings": [
{
"length": 10,
"offset": 0
}
],
"secondary_text": "Kensington, London, UK"
},
"terms": [
{
"offset": 0,
"value": "Gloucester Road"
},
{
"offset": 17,
"value": "Kensington"
},
{
"offset": 29,
"value": "London"
},
{
"offset": 37,
"value": "UK"
}
],
"types": [
"route",
"geocode"
]
},
{
"description": "Gloucester Road Station, Gloucester Road, London, UK",
"matched_substrings": [
{
"length": 10,
"offset": 0
}
],
"place_id": "ChIJEVVNPV4FdkgREWjenJPqiBI",
"reference": "ChIJEVVNPV4FdkgREWjenJPqiBI",
"structured_formatting": {
"main_text": "Gloucester Road Station",
"main_text_matched_substrings": [
{
"length": 10,
"offset": 0
}
],
"secondary_text": "Gloucester Road, London, UK"
},
"terms": [
{
"offset": 0,
"value": "Gloucester Road Station"
},
{
"offset": 25,
"value": "Gloucester Road"
},
{
"offset": 42,
"value": "London"
},
{
"offset": 50,
"value": "UK"
}
],
"types": [
"premise",
"geocode"
]
},
{
"description": "Gloucester Green, Oxford, UK",
"matched_substrings": [
{
"length": 10,
"offset": 0
}
],
"place_id": "ChIJ3TUITKTGdkgRF0rXBjwx5A8",
"reference": "ChIJ3TUITKTGdkgRF0rXBjwx5A8",
"structured_formatting": {
"main_text": "Gloucester Green",
"main_text_matched_substrings": [
{
"length": 10,
"offset": 0
}
],
"secondary_text": "Oxford, UK"
},
"terms": [
{
"offset": 0,
"value": "Gloucester Green"
},
{
"offset": 18,
"value": "Oxford"
},
{
"offset": 26,
"value": "UK"
}
],
"types": [
"neighborhood",
"political",
"geocode"
]
}
]
Geocode lookup
The following API endpoint returns latitude and longitude information for a given address.
https://prod.kfcapi.com/api/v1/util/google/geocode?address=<LOCATION>&geotype=Google&autorefid={autorefid}
Valid examples for <LOCATION>
include:
- 'Gloucester, UK' (URL encoded:
Gloucester,%20UK
) - 'GL1 2TG' (URL encoded:
GL1%202TG
)
Example
$ curl --compressed 'https://prod.kfcapi.com/api/v1/util/google/geocode?address=Gloucester,%20UK&geotype=Google&autorefid={autorefid}' \
-H 'x-api-key: siYAzKattmaIHSwMV9OJYtaoP8SRq' \
-H 'codemarket: UK' \
-H 'countrycode: GB'
{
"location": {
"lat": 51.8642449,
"lng": -2.238156
},
"countrycode": "GB",
"countryname": "United Kingdom",
"deliveryAddressObject": {
"street": "",
"posttown": "Gloucester",
"postcode": "",
"fullAddress": "Gloucester, UK"
}
}
KFC locations (2)
https://prod.kfcapi.com/api/v3/restaurants/geocode
Example
$ curl --compressed -X POST 'https://prod.kfcapi.com/api/v3/restaurants/geocode' \
-H 'Content-Type: application/json' \
-H 'x-api-key: siYAzKattmaIHSwMV9OJYtaoP8SRq' \
-H 'codemarket: UK' \
-H 'countrycode: GB' \
--data-raw '{"lat":51.8642,"lng":-2.2381}'
[
{
"refid": "77064004",
"distance": 1672.74773966
},
{
"refid": "77064003",
"distance": 3875.53003224
},
{
"refid": "77064006",
"distance": 8176.30920337
},
{
"refid": "77064005",
"distance": 11071.95785298
},
{
"refid": "77064009",
"distance": 11990.77038952
},
{
"refid": "77023843",
"distance": 15495.2865017
},
{
"refid": "77064008",
"distance": 23880.31031652
},
{
"refid": "77064010",
"distance": 25976.74317347
},
{
"refid": "77010121",
"distance": 26523.97636434
},
{
"refid": "77010131",
"distance": 26587.65156733
},
{
"refid": "77103441",
"distance": 36257.46257524
},
{
"refid": "77106025",
"distance": 37969.78802345
},
{
"refid": "77103438",
"distance": 38723.14959847
},
{
"refid": "77064007",
"distance": 38815.62381749
},
{
"refid": "77098011",
"distance": 39627.43915774
}
]
Restaurant details
$ curl --compressed 'https://prod.kfcapi.com/api/v3/restaurants/77064003' \
-H 'x-api-key: siYAzKattmaIHSwMV9OJYtaoP8SRq' \
-H 'codemarket: UK' \
-H 'countrycode: GB'
{
"name": "Gloucester - Goodridge Avenue",
"street": "Goodridge Avenue",
"city": "Quedgely",
"postalcode": "GL2 5EA",
"marketCode": "UK",
"countryCode": "GB",
"geolocation": {
"longitude": -2.2731001,
"latitude": 51.8368626
},
"refid": "77064003",
"status": "available",
"judopayid": "100126184",
"region": "South West",
"storeid": "6403",
"bagtaxtype": "NONE",
"defaultEtaMin": 40,
"defaultEtaMax": 60,
"hours": [
{
"type": "Standard",
"groupId": "c3d9b5bd-b6bf-4351-97b2-3a0cbde14f47",
"startDate": null,
"endDate": null,
"thursday": {
"open": 900,
"close": 2300
},
"friday": {
"open": 900,
"close": 2300
},
"wednesday": {
"open": 900,
"close": 2300
},
"tuesday": {
"open": 900,
"close": 2300
},
"sunday": {
"open": 900,
"close": 2300
},
"monday": {
"open": 900,
"close": 2300
},
"saturday": {
"open": 900,
"close": 2300
}
},
{
"type": "Drivethru",
"groupId": "801aed17-73cc-4eb8-8dcd-1a3c8c53201d",
"startDate": null,
"endDate": null,
"monday": {
"open": 900,
"close": 100
},
"saturday": {
"open": 900,
"close": 100
},
"wednesday": {
"open": 900,
"close": 100
},
"sunday": {
"open": 900,
"close": 100
},
"thursday": {
"open": 900,
"close": 100
},
"friday": {
"open": 900,
"close": 100
},
"tuesday": {
"open": 900,
"close": 100
}
},
{
"type": "Collect",
"groupId": "a4f85c65-0250-4993-b131-60442c303255",
"startDate": null,
"endDate": null,
"wednesday": {
"open": 900,
"close": 2245
},
"tuesday": {
"open": 900,
"close": 2245
},
"sunday": {
"open": 900,
"close": 2245
},
"monday": {
"open": 900,
"close": 2245
},
"friday": {
"open": 900,
"close": 2245
},
"thursday": {
"open": 900,
"close": 2245
},
"saturday": {
"open": 900,
"close": 2245
}
},
{
"type": "Delivery",
"groupId": "d2bfcb0d-e825-4ebf-8a20-3988cc6caa1c",
"startDate": null,
"endDate": null,
"monday": {
"open": 1000,
"close": 2345
},
"sunday": {
"open": 1000,
"close": 2345
},
"friday": {
"open": 1000,
"close": 2345
},
"thursday": {
"open": 1000,
"close": 2345
},
"wednesday": {
"open": 1000,
"close": 2345
},
"tuesday": {
"open": 1000,
"close": 2345
},
"saturday": {
"open": 1000,
"close": 2345
}
},
{
"type": "Uber",
"groupId": "4200c603-d10e-4b59-9f80-a383693c817c",
"startDate": null,
"endDate": null,
"friday": {
"open": 1000,
"close": 2345
},
"wednesday": {
"open": 1000,
"close": 2345
},
"tuesday": {
"open": 1000,
"close": 2345
},
"sunday": {
"open": 1000,
"close": 2345
},
"monday": {
"open": 1000,
"close": 2345
},
"saturday": {
"open": 1000,
"close": 2345
},
"thursday": {
"open": 1000,
"close": 2345
}
},
{
"type": "Deliveroo",
"groupId": "d5b5d554-9b6e-49a0-8b9c-c1a4b318daf3",
"startDate": null,
"endDate": null,
"wednesday": {
"open": 1000,
"close": 2345
},
"tuesday": {
"open": 1000,
"close": 2345
},
"sunday": {
"open": 1000,
"close": 2345
},
"friday": {
"open": 1000,
"close": 2345
},
"thursday": {
"open": 1000,
"close": 2345
},
"monday": {
"open": 1000,
"close": 2345
},
"saturday": {
"open": 1000,
"close": 2345
}
},
{
"type": "Justeat",
"groupId": "1b27e17c-4634-4df9-97ed-a9d917b80c29",
"startDate": null,
"endDate": null,
"tuesday": {
"open": 1000,
"close": 2345
},
"sunday": {
"open": 1000,
"close": 2345
},
"friday": {
"open": 1000,
"close": 2345
},
"wednesday": {
"open": 1000,
"close": 2345
},
"monday": {
"open": 1000,
"close": 2345
},
"saturday": {
"open": 1000,
"close": 2345
},
"thursday": {
"open": 1000,
"close": 2345
}
}
],
"facilities": [
{
"name": "Drive Thru",
"imagePath": "restaurants/facilities-icons/restaurant-facilities-drive-thru.png"
},
{
"name": "Baby Changing Room",
"imagePath": "restaurants/facilities-icons/restaurant-facilities-baby-change.png"
},
{
"name": "Vegan",
"imagePath": "restaurants/facilities-icons/restaurant-facilities-vegan.png"
},
{
"name": "Mobile Ordering",
"imagePath": "restaurants/facilities-icons/restaurant-collect.png"
},
{
"name": "Zinger Popcorn",
"imagePath": "restaurants/facilities-icons/restaurant-facilities-zinger-popcorn.png"
},
{
"name": "Free Wifi",
"imagePath": "restaurants/facilities-icons/restaurant-facilities-wifi.png"
},
{
"name": "Disability Access",
"imagePath": "restaurants/facilities-icons/restaurant-facilities-disabled-access.png"
},
{
"name": "Colonel's Club",
"imagePath": "restaurants/facilities-icons/restaurant-facilities-colonels-club.png"
}
],
"orderModes": [
{
"modeType": "ClickAndCollect",
"serviceType": "Drivethru"
},
{
"modeType": "Deliveroo",
"serviceType": "TakeAway"
},
{
"modeType": "Delivery",
"serviceType": "TakeAway"
},
{
"modeType": "JustEat",
"serviceType": "TakeAway"
},
{
"modeType": "ClickAndCollect",
"serviceType": "DineIn"
},
{
"modeType": "UberEats",
"serviceType": "TakeAway"
},
{
"modeType": "ClickAndCollect",
"serviceType": "TakeAway"
}
]
}
Menu
'https://prod.kfcapi.com/api/v3/restaurants/<REFID>/menu?modeType=ClickAndCollect&serviceType=TakeAway'
Example
$ curl 'https://prod.kfcapi.com/api/v3/restaurants/77064003/menu?modeType=ClickAndCollect&serviceType=TakeAway' \
-H 'x-api-key: siYAzKattmaIHSwMV9OJYtaoP8SRq' \
-H 'codemarket: UK' \
-H 'countrycode: GB' \
{
"channel": "ClickAndCollect",
"menuUrl": "https://menu-output-prod.s3.eu-west-1.amazonaws.com/77064003-ClickAndCollect.json",
"lastModifiedDate": "2023-04-01T05:04:10.000Z"
}