Once your data is embedded, "find similar items" becomes "find nearby vectors." But "nearby" is an ambiguous word in a high-dimensional space, and there's more than one way to measure it.
The three metrics you'll actually run into for text and image embeddings are cosine similarity, Euclidean distance, and dot product. They sound interchangeable, and on normalized vectors they almost are, but the practical defaults are not arbitrary. Here's how they differ and when to deviate.
The three metrics

Cosine similarity measures the angle between two vectors and ignores how long either one is. Two vectors pointing in the same direction score 1.0; two perpendicular vectors score 0.0; two pointing in opposite directions score -1.0. For normalized embeddings (which most modern text models produce), scores cluster between 0 and 1 in practice. Vector Panda reports cosine as a 0-1 number for ease of reading.
Euclidean distance treats each vector as a point in space and measures the literal straight-line distance between two points. Two identical vectors have distance 0; the further apart they are in space, the larger the number. Unlike cosine, Euclidean distance considers magnitude: a long vector and a short vector pointing in the same direction will have a non-zero distance.
Dot product is the sum of pairwise products of the two vectors' components. It's related to cosine: the dot product equals cosine times the lengths of both vectors. On normalized vectors (length 1), dot product and cosine are mathematically identical. On unnormalized vectors, dot product weights longer vectors as more similar.
Why cosine is the default

Three things conspired to make cosine the standard for text-embedding search.
The first is that most modern text models produce normalized vectors as output. The model designers made that choice on purpose because it makes downstream geometry behave well. When everything is normalized, cosine and Euclidean and dot product all rank items in nearly the same order; cosine is just the most interpretable of the three (0 to 1, "how similar in meaning").
The second is that magnitude usually carries no meaning for text. A document is not "more about" something because its embedding happens to have a larger length. Cosine throws magnitude away, which is the right move when magnitude is noise.
The third is sheer momentum. The original SBERT paper used cosine; OpenAI's embedding API recommends cosine; the MTEB benchmark scores use cosine. Models are trained, evaluated, and documented under the assumption that you'll measure with cosine. Switching to a different metric without a strong reason means you're optimizing for something the model wasn't trying to give you.
When other metrics make sense
There are real cases where cosine is the wrong choice.
The clearest is recommendation systems where popularity is encoded in vector magnitude. Imagine a system where each product's embedding has been multiplied by some popularity score: bestsellers get long vectors, obscure items get short ones. Cosine ignores that signal entirely; Euclidean and dot product preserve it. If you've gone to the trouble of baking magnitude into your vectors, use a metric that respects it.

A second case is dot product on normalized vectors when speed matters. Cosine requires a normalization step at query time (or precomputed). Dot product on already-normalized vectors gives you the same ranking with one less operation per comparison. The difference is microseconds per query, which only matters at very high QPS, but it's there.
A third is binary embeddings, where each vector is a string of 0s and 1s instead of floats. Hamming distance (count of differing bits) is the appropriate metric here, and it's much faster than any of the float-vector options. This is the path you take when memory pressure matters more than the last few percent of recall.

Closing
Pick cosine unless you have a specific reason not to. The reason will look like one of: your embedding model's documentation tells you to use something else, your vectors carry magnitude as signal, or you're working with binary embeddings. In every other case, cosine is the right default and you shouldn't think about it twice. The hard work of meaning-encoding happened upstream, in the model; the metric just needs to play it back faithfully.
