Restaurant Recommendations

🛠️

Public Beta Feature

This documentation covers a feature currently in Public Beta. Access is available to anyone interested in building personalized experiences for their end-users.

This feature is subject to the Personalization API (Self-Service) Public Beta End user License Agreement 📄.

Curate a Unique Gastro Journey

Use our Movement SDK to reach users with the right messaging at the right place and time, whether they want quick takeout/delivery or “foodies” who are searching for a planned in-advance dinner.

Jump-start personalization during user app onboarding by integrating our Tastes endpoints with your customer data platform to maintain a detailed user persona profile based on user interests and preferences.

Using our Search endpoints, enhance customer trust by showing venue recommendations tailored to their unique interests and preferences.





Features Used

Our restaurant recommendation mobile app use case leverages the following features to achieve the afore-mentioned user experience.

Movement SDK

  • Snap to Place Technology to generate visit events and feed the recommendations algorithm
  • Geofences for event detection on a specified set of lat/longs, POIs, categories, or chains.

Personalization API Endpoints

  • Tastes to identify a user’s preferences - cuisine, specific dishes or drinks, ambience, suitability for specific occasions - to jump start personalization.
  • Search to inspire your users to discover new places around them.

Get Started

Step 1. Set up Your FSQ Developer Account

Step 2. Install and Configure the Movement SDK

Step 3. Build the API Calls

Step 4. Integrate API Calls Into Your App Code

The following code written in Swift for an iOS app provides an example of how to call Foursquare’s Tastes endpoints to understand their preferences and make venue recommendations based on those preferences..

Please make sure to include the unique per app user oauth_token to ensure a personalized experience. Learn more about Foursquare’s User-ful Authentication.

import Foundation

let v = "20231020"
let oauthToken = "[TOKEN]"

struct FoursquareResponse<ResponseType: Codable>: Codable {
    struct Meta: Codable {
        let code: Int
        let requestId: String
    }


    let meta: Meta
    let response: ResponseType
}

struct EmptyResponse: Codable {


}

struct HTTPError: Error {
    let statusCode: Int


    static let badRequest = HTTPError(statusCode: 400)
    // ... more statuses
}

@discardableResult
func makeRequest(endpoint: String, method: String, params: [String: String]) async throws -> Data {
    let components = {
        var components = URLComponents(string: "https://api.foursquare.com/v2\(endpoint)")!
        components.queryItems = [
            URLQueryItem(name: "v", value: v),
            URLQueryItem(name: "oauth_token", value: oauthToken)
        ] + params.map { URLQueryItem(name: $0.key, value: $0.value) }
        return components
    }()

    let request = {
        var request = URLRequest(url: components.url!)
        request.httpMethod = method
        return request
    }()

    let result = try await URLSession.shared.data(for: request)
    guard let response = result.1 as? HTTPURLResponse, response.statusCode < 400 else {
        throw HTTPError(statusCode: (result.1 as? HTTPURLResponse)?.statusCode ?? HTTPError.badRequest.statusCode)
    }
    return result.0
}

struct Venue: Codable {
    let id: String
    let name: String
}

struct User: Codable {
    let id: String
    let firstName: String
    let lastName: String
//    ...
}

struct Taste: Codable {
    let id: String
    let text: String
}

struct GetTasteSuggestionsResponse: Codable {
    let tastes: [Taste]
}

struct GetVenueRecommendationsResponse: Codable {
    struct Group: Codable {
        struct Result: Codable {
            let venue: Venue
        }
        let results: [Result]
    }
    let group: Group
}

func getTasteSuggestions(intent: String) async throws -> [Taste] {
    let data = try await makeRequest(endpoint: "/tastes/suggestions", method: "GET", params: ["intent": intent])
    return try JSONDecoder().decode(FoursquareResponse<GetTasteSuggestionsResponse>.self, from: data).response.tastes
}

func addTaste(taste: Taste) async throws {
    try await makeRequest(endpoint: "/tastes/add", method: "POST", params: ["tasteId": taste.id])
}

func getVenueRecommendations(latLng: String) async throws -> GetVenueRecommendationsResponse {
    let data = try await makeRequest(endpoint: "/search/recommendations", method: "GET", params: ["ll": latLng])
    return try JSONDecoder().decode(FoursquareResponse<GetVenueRecommendationsResponse>.self, from: data).response
}

let tastes = try await getTasteSuggestions(intent: "tipstream")
if let taste = tastes.first {
    try await addTaste(taste: taste)
}
let latLng = "40.7591622,-74.0516322"
print(try await getVenueRecommendations(latLng: latLng))