Embedding Recommendation Endpoints

Introduction: Why Embedding-Based Endpoints Matter

Welcome back! In the last few lessons, you learned how to turn music tracks and user preferences into vectors, use cosine similarity for recommendations, and group tracks into clusters. Now, you are ready to see how all these pieces come together in a real-world application.

In this lesson, you will learn how to expose your recommendation logic through API endpoints. These endpoints allow your music app to deliver personalized track suggestions, show how tracks are grouped, and let users inspect their own listening profiles.

Before jumping into the code, it’s helpful to understand the purpose of each endpoint:

  • A recommendation endpoint helps deliver real-time track suggestions.
  • A cluster summary endpoint lets users or developers explore how the music library is organized.
  • A user profile endpoint exposes the internal representation (embedding) of a user’s taste — useful for debugging or building transparency features.

These endpoints don’t just return data — they act as bridges between the machine learning logic and a real frontend or client.

Quick Recap: App Structure and Data Loading

Before we dive into the new endpoints, let’s quickly remind ourselves how the app is set up. You have a Flask application that loads track data, computes embeddings, and prepares everything needed for recommendations. Here’s a summary of the setup:

from flask import Flask
from src.database import get_all_tracks
from src.user_model import get_track_embeddings
from src.clustering import assign_track_clusters

app = Flask(__name__)

# Initialize data and embeddings
get_all_tracks()            # Loads tracks into memory
get_track_embeddings()      # Computes and caches track embeddings
assign_track_clusters()     # Computes and caches track clusters

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5001, debug=True)

This code ensures that your app is ready to serve recommendations as soon as it starts. If you are using CodeSignal, these libraries and data will already be set up for you.

Embedding-Based Recommendation Endpoint

The main endpoint for delivering personalized track suggestions is:

GET /api/recommendations/embedding/<user_id>?top_n=5

Let’s break down how this works:

  • <user_id>: The ID of the user you want recommendations for.
  • top_n (optional): How many recommendations to return (the default is 5).

Here’s the relevant code:

@app.route('/api/recommendations/embedding/<user_id>', methods=['GET'])
def get_embedding_recommendations_endpoint(user_id: str):
    try:
        top_n_str = request.args.get('top_n', default='5')
        top_n = int(top_n_str)
        if top_n <= 0:
            return jsonify({"error": "top_n must be a positive integer"}), 400
    except ValueError:
        return jsonify({"error": "top_n must be a valid integer"}), 400

    recommended_track_ids = recommend_tracks_by_similarity(user_id, top_n=top_n)
    
    if not recommended_track_ids:
        return jsonify({
            "user_id": user_id,
            "recommendations": [],
            "message": "No recommendations available for this user (perhaps new user or no suitable tracks)."
        }), 200

    recommended_tracks_details = []
    for track_id in recommended_track_ids:
        track_details = get_track_by_id(track_id)
        if track_details:
            recommended_tracks_details.append(track_details)
            
    return jsonify({
        "user_id": user_id,
        "recommendations": recommended_tracks_details
    })

What happens here?

  1. The endpoint reads the top_n parameter and checks if it’s a valid positive integer.
  2. It calls recommend_tracks_by_similarity(user_id, top_n) to get the best track IDs for the user.
  3. If there are no recommendations (for example, if the user is new), it returns an empty list with a helpful message. This case is known as the cold start problem in recommendation systems. Since the system doesn't yet know the user’s preferences, it can't generate a profile vector. In a production app, you'd typically fall back to popular tracks or ask the user to rate a few songs first.
  4. Otherwise, it fetches the full details for each recommended track and returns them in a JSON response.

As you remember from the previous units, the endpoint relies on a function called recommend_tracks_by_similarity, which compares the user’s profile vector against all track embeddings using cosine similarity. This gives each track a similarity score — how close it is to the user’s taste — and returns the top matches.

Note that tracks the user has already listened to are excluded from the final results. This is handled inside the recommendation logic and ensures users only see new content.

Example output:

{
  "user_id": "user_123",
  "recommendations": [
    {"id": "track_7", "title": "Dreamscape", "genre": "Ambient"},
    {"id": "track_2", "title": "Night Drive", "genre": "Synthwave"},
    {"id": "track_5", "title": "Sunrise", "genre": "Pop"}
  ]
}

If the user has no listening history:

{
  "user_id": "user_123",
  "recommendations": [],
  "message": "No recommendations available for this user (perhaps new user or no suitable tracks)."
}

This endpoint is the main way your app delivers personalized music suggestions.

Sign up

Join the 1M+ learners on CodeSignal

Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal