> ## Documentation Index
> Fetch the complete documentation index at: https://algolia.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Prevent typosquatting

> How to prevent typosquatting using Algolia's sort-by feature.

export const Application = () => <Tooltip tip="An Algolia application is a self-contained environment with its own indices, configuration, and API keys. Applications don't share data or settings with each other.">
    application
  </Tooltip>;

export const AlgoliaSearch = () => <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 80 80" width="20" height="20" className="inline" fill="none" role="presentation" ariaLabel="Algolia Search">
    <circle cx="40" cy="32" r="28" fill="#5468FF"></circle>
    <rect x="30" y="22" width="20" height="20" rx="10" fill="#fff"></rect>
    <path d="M43 63.5 54.5 60l6 17h-12L43 63.5Z" fill="#36395A"></path>
  </svg>;

Algolia and other search engines prefer exact matches.
Sometimes, this is used to **take advantage of people's typing mistakes and get ranked high on a popular search query**.

For example, consider Twitter users.
An example of typosquatting is the account @BarakObama,
which has 15,800 followers, but isn't @BarackObama (Barack Obama's official account).
Because Algolia prioritizes exact matches,
typing "BarakObama" would return the "BarakObama" record first,
regardless of custom ranking.

**Not all use cases need to prevent typosquatting**.

However, if this is your case,
which often happens when you have to deal with user-generated content,
you may need to put a strategy in place.

## Dataset example

Back to the Twitter example: assume you have an index called `twitter_accounts` that looks like this:

```json JSON icon=braces theme={"system"}
[
  {
    "twitter_handle": "BarackObama",
    "nb_followers": 103500000
  },
  {
    "twitter_handle": "BarakObama",
    "nb_followers": 15800
  }
]
```

Even if you set descending custom ranking on `nb_followers`,
because Algolia prioritizes exact results,
the @BarakObama account would benefit from the traffic coming from users making a typo when searching for the official Barack Obama account.

You can short-circuit this issue with Algolia's [sort-by-attribute feature](/doc/guides/managing-results/refine-results/sorting/how-to/sort-by-attribute).

## Update the dataset

The recommended solution is to add a boolean attributes that separates popular records from the rest.
For example, you could add something like `is_verified_account = true`,
or `is_popular = true`, and sort on that attribute.

<Tip>
  For this approach to work well, **the number of records with `is_popular` or `is_verified_account`
  set to `true` should be a small subset of the dataset** (up to 1% of the dataset).
</Tip>

You have a popularity metric (`nb_followers`),
so you can use it to define a rule that determines if a record is popular or not.
In this example, you could say that a user is popular if they have more than a million followers.

To run the code examples on this page, [install the latest API client](/doc/libraries/sdk/install).
You can use the [`browse`](/doc/libraries/sdk/v1/methods/browse) method to update the index:

<CodeGroup>
  ```cs C# theme={"system"}
  namespace Algolia;

  using System;
  using System.Collections.Generic;
  using System.Net.Http;
  using System.Text.Json;
  using Algolia.Search.Clients;
  using Algolia.Search.Http;
  using Algolia.Search.Models.Search;

  class SavePopularRecords
  {
    class Record
    {
      public int NbFollowers { get; set; }
      public required string TwitterHandle { get; set; }
      public bool IsPopular { get; set; }
    }

    async Task Main(string[] args)
    {
      var client = new SearchClient(new SearchConfig("ALGOLIA_APPLICATION_ID", "ALGOLIA_API_KEY"));

      var hits = await client.BrowseObjectsAsync<Record>(
        "INDEX_NAME",
        new BrowseParamsObject()
      );

      var records = hits.Select(hit => new Record
        {
          TwitterHandle = hit.TwitterHandle,
          NbFollowers = hit.NbFollowers,
          IsPopular = hit.NbFollowers >= 1_000_000,
        })
        .ToList();

      await client.SaveObjectsAsync("INDEX_NAME", records);
    }
  }

  ```

  ```dart Dart theme={"system"}
  import 'package:algolia_client_search/algolia_client_search.dart';

  void savePopularRecords() async {
    final client =
        SearchClient(appId: 'ALGOLIA_APPLICATION_ID', apiKey: 'ALGOLIA_API_KEY');

    final List<Map<String, dynamic>> records = [];

    BrowseResponse? browseResponse;
    do {
      final browseParams =
          BrowseParamsObject(hitsPerPage: 1000, cursor: browseResponse?.cursor);
      browseResponse =
          await client.browse(indexName: "indexName", browseParams: browseParams);
      for (final hit in browseResponse.hits) {
        final props = hit.toJson();
        final isPopular = props['nbFollowers'] > 1000;
        final updatedProduct = {
          ...props,
          'isPopular': isPopular,
        };
        records.add(updatedProduct);
      }
    } while (browseResponse.cursor != null);

    final batchParams = BatchWriteParams(
        requests: records
            .map((record) => BatchRequest(
                  action: Action.addObject,
                  body: record,
                ))
            .toList());
    await client.batch(
      indexName: "INDEX_NAME",
      batchWriteParams: batchParams,
    );
  }

  ```

  ```go Go theme={"system"}
  package main

  import (
  	"github.com/algolia/algoliasearch-client-go/v4/algolia/search"
  )

  func savePopularRecords() {
  	client, err := search.NewClient("ALGOLIA_APPLICATION_ID", "ALGOLIA_API_KEY")
  	if err != nil {
  		// The client can fail to initialize if you pass an invalid parameter.
  		panic(err)
  	}

  	records := []map[string]any{{ /* Your records */ }}

  	err = client.BrowseObjects("INDEX_NAME", search.BrowseParamsObject{}, search.WithAggregator(func(res any, err error) {
  		if err != nil {
  			panic(err)
  		}

  		browseRes, ok := res.(search.BrowseResponse)
  		if !ok {
  			panic("Invalid response")
  		}

  		for _, hit := range browseRes.Hits {
  			record := hit.AdditionalProperties
  			record["isPopular"] = record["nbFollowers"].(int) >= 1_000_000

  			records = append(records, record)
  		}
  	}))
  	if err != nil {
  		panic(err)
  	}

  	_, err = client.SaveObjects(
  		"INDEX_NAME", records)
  	if err != nil {
  		panic(err)
  	}
  }

  ```

  ```java Java theme={"system"}
  package com.algolia;

  import com.algolia.api.SearchClient;
  import com.algolia.config.*;
  import com.algolia.model.search.*;
  import java.util.ArrayList;
  import java.util.HashMap;
  import java.util.List;
  import java.util.Map;

  public class savePopularRecords {

    public static void main(String[] args) throws Exception {
      try (SearchClient client = new SearchClient("ALGOLIA_APPLICATION_ID", "ALGOLIA_API_KEY")) {
        List<Map<String, Object>> records = new ArrayList<>();

        client
          .browseObjects("INDEX_NAME", Hit.class)
          .forEach(hit -> {
            Map<String, Object> props = hit.getAdditionalProperties();
            int nbFollowers = (int) props.get("nbFollowers");

            Map<String, Object> record = new HashMap<>();
            record.put("twitterHandle", props.get("twitterHandle"));
            record.put("nbFollowers", nbFollowers);
            record.put("isPopular", nbFollowers >= 1_000_000);

            records.add(record);
          });

        client.saveObjects("INDEX_NAME", records);
      } catch (Exception e) {
        System.out.println("An error occurred: " + e.getMessage());
      }
    }
  }

  ```

  ```js JavaScript theme={"system"}
  import { algoliasearch } from 'algoliasearch';

  const client = algoliasearch('ALGOLIA_APPLICATION_ID', 'ALGOLIA_API_KEY');

  const records: Array<Record<string, any>> = [];

  await client.browseObjects<Record<string, any>>({
    indexName: 'INDEX_NAME',
    browseParams: undefined,
    aggregator: (res) =>
      records.push(
        res.hits.map((record) => ({
          ...record,
          isPopular: record.nbFollowers >= 1_000_000,
        })),
      ),
  });

  await client.saveObjects({ indexName: 'indexName', objects: records });

  ```

  ```kotlin Kotlin theme={"system"}
  import com.algolia.client.api.SearchClient
  import com.algolia.client.configuration.*
  import com.algolia.client.extensions.*
  import com.algolia.client.model.search.*
  import com.algolia.client.transport.*
  import kotlinx.serialization.json.JsonObject
  import kotlinx.serialization.json.JsonPrimitive
  import kotlinx.serialization.json.buildJsonObject

  suspend fun savePopularRecords() {
    val client = SearchClient(appId = "ALGOLIA_APPLICATION_ID", apiKey = "ALGOLIA_API_KEY")

    var records: List<JsonObject> = listOf()

    client.browseObjects(
      "INDEX_NAME",
      BrowseParamsObject(),
      aggregator = { response ->
        records =
          records +
            response.hits.map { hit ->
              val props = hit.additionalProperties ?: mapOf()

              val nbFollowers = props["nbFollowers"].toString().toInt()

              buildJsonObject {
                put("twitterHandle", JsonPrimitive(props["twitterHandle"].toString()))
                put("nbFollowers", JsonPrimitive(nbFollowers))
                put("isPopular", JsonPrimitive(nbFollowers > 1000000))
              }
            }
      },
    )
    client.saveObjects(indexName = "INDEX_NAME", objects = records)
  }

  ```

  ```php PHP theme={"system"}
  <?php

  require __DIR__.'/../vendor/autoload.php';
  use Algolia\AlgoliaSearch\Api\SearchClient;

  $client = SearchClient::create('ALGOLIA_APPLICATION_ID', 'ALGOLIA_API_KEY');

  $results = $client->browseObjects('INDEX_NAME');

  $records = [];
  foreach ($results as $hit) {
      $records[] = [
          'twitterHandle' => $hit['twitterHandle'],
          'nbFollowers' => $hit['nbFollowers'],
          'isPopular' => $hit['nbFollowers'] > 1000000,
      ];
  }

  $client->saveObjects(
      'INDEX_NAME',
      $records,
  );

  ```

  ```python Python theme={"system"}
  from algoliasearch.search.client import SearchClientSync

  from algoliasearch.search.models.browse_response import BrowseResponse


  _client = SearchClientSync("ALGOLIA_APPLICATION_ID", "ALGOLIA_API_KEY")


  records = []


  def _aggregator(resp: BrowseResponse):
      for hit in resp.hits:
          hit_dict = hit.to_dict()
          records.append(
              {
                  "twitterHandle": hit_dict["twitterHandle"],
                  "nbFollowers": hit_dict["nbFollowers"],
                  "isPopular": hit_dict["nbFollowers"] > 1000000,
              }
          )


  _client.browse_objects(
      index_name="INDEX_NAME",
      aggregator=_aggregator,
  )

  _client.save_objects(
      index_name="INDEX_NAME",
      objects=records,
  )

  ```

  ```ruby Ruby theme={"system"}
  require "algolia"

  client = Algolia::SearchClient.create("ALGOLIA_APPLICATION_ID", "ALGOLIA_API_KEY")

  records = []

  client.browse_objects(
    index_name = "INDEX_NAME"
  ) do |resp|
    resp.hits.each { |hit|
      record = hit
      record["isPopular"] = hit["nbFollowers"] > 1000000
      records.append(record)
    }
  end

  client.save_objects("INDEX_NAME", records)

  ```

  ```scala Scala theme={"system"}
  import scala.concurrent.Future
  import scala.concurrent.ExecutionContext.Implicits.global

  import algoliasearch.api.SearchClient
  import algoliasearch.config.*
  import algoliasearch.extension.SearchClientExtensions
  import algoliasearch.search.{BrowseParamsObject, BrowseResponse}

  case class Record(
      twitterHandle: String,
      nbFollowers: Int,
      isPopular: Boolean
  )

  def savePopularRecords(): Future[Unit] = {
    val client = SearchClient(appId = "ALGOLIA_APPLICATION_ID", apiKey = "ALGOLIA_API_KEY")

    var records: Seq[Record] = Seq.empty

    client
      .browseObjects(
        "INDEX_NAME",
        BrowseParamsObject(),
        aggregator = (response: BrowseResponse) => {
          records = records ++ response.hits.map { hit =>
            val props = hit.additionalProperties.getOrElse(List()).toMap

            var nbFollowers = props("nbFollowers").toString.toInt

            Record(
              twitterHandle = props("twitterHandle").toString,
              nbFollowers = nbFollowers,
              isPopular = nbFollowers > 1000000
            )
          }
        }
      )
      .flatMap { _ =>
        client.saveObjects(
          indexName = "INDEX_NAME",
          objects = records
        )

        Future.unit
      }
      .recover { case ex: Exception =>
        println(s"An error occurred: ${ex.getMessage}")
      }
  }

  ```

  ```swift Swift theme={"system"}
  import Foundation
  #if os(Linux) // For linux interop
      import FoundationNetworking
  #endif

  import AlgoliaCore
  import AlgoliaSearch

  struct Record: Codable {
      let twitterHandle: String
      let nbFollowers: Int
      var isPopular = false
  }

  func savePopularRecords() async throws {
      let client = try SearchClient(appID: "ALGOLIA_APPLICATION_ID", apiKey: "ALGOLIA_API_KEY")

      var records: [Record] = []

      try await client.browseObjects(
          indexName: "INDEX_NAME",
          browseParams: BrowseParamsObject(),
          aggregator: { (response: BrowseResponse<Record>) in
              records.append(contentsOf: response.hits.map {
                  $0.nbFollowers < 1_000_000 ? $0 : Record(
                      twitterHandle: $0.twitterHandle,
                      nbFollowers: $0.nbFollowers,
                      isPopular: true
                  )
              })
          }
      )

      try await client.saveObjects(indexName: "INDEX_NAME", objects: records)
  }

  ```
</CodeGroup>

After the update, your dataset would look like this:

```json JSON icon=braces theme={"system"}
[
  {
    "twitter_handle": "BarackObama",
    "nb_followers": 103500000,
    "is_popular": true
  },
  {
    "twitter_handle": "BarakObama",
    "nb_followers": 15800,
    "is_popular": false
  }
]
```

By default, Typo is the first criterion in [Algolia's ranking formula](/doc/guides/managing-results/relevance-overview/in-depth/ranking-criteria) which, for most use cases, is sensible.

To prevent typosquatting,
you need to add another ranking signal that's higher than the `typo` rule—the sort-by attribute.

When your ranking has been applied,
searching for "BarakObama" returns the "BarackObama" record first.

## Add a sort-by attribute in the dashboard

1. Go to the [Algolia dashboard](https://dashboard.algolia.com/explorer/browse) and select your Algolia <Application />.
2. On the left sidebar, select <AlgoliaSearch /> **Search**.
3. Select your Algolia index.
4. On the **Configuration** tab, select **Ranking and Sorting**.
5. Click **Add sort-by attribute** and type or select your attributes.
6. Save your changes.

## Add a sort-by attribute with the API

To set a sort-by attribute,
you need to use the [`ranking`](/doc/api-reference/api-parameters/ranking) with the [`setSettings`](/doc/libraries/sdk/v1/methods/set-settings) method.

<CodeGroup>
  ```cs C# theme={"system"}
  var response = await client.SetSettingsAsync(
    "INDEX_NAME",
    new IndexSettings
    {
      Ranking = new List<string>
      {
        "desc(is_popular)",
        "typo",
        "geo",
        "words",
        "filters",
        "proximity",
        "attribute",
        "exact",
        "custom",
      },
    }
  );
  ```

  ```dart Dart theme={"system"}
  final response = await client.setSettings(
    indexName: "INDEX_NAME",
    indexSettings: IndexSettings(
      ranking: [
        "desc(is_popular)",
        "typo",
        "geo",
        "words",
        "filters",
        "proximity",
        "attribute",
        "exact",
        "custom",
      ],
    ),
  );
  ```

  ```go Go theme={"system"}
  response, err := client.SetSettings(client.NewApiSetSettingsRequest(
    "INDEX_NAME",
    search.NewEmptyIndexSettings().SetRanking(
      []string{"desc(is_popular)", "typo", "geo", "words", "filters", "proximity", "attribute", "exact", "custom"})))
  if err != nil {
    // handle the eventual error
    panic(err)
  }
  ```

  ```java Java theme={"system"}
  UpdatedAtResponse response = client.setSettings(
    "INDEX_NAME",
    new IndexSettings().setRanking(
      Arrays.asList("desc(is_popular)", "typo", "geo", "words", "filters", "proximity", "attribute", "exact", "custom")
    )
  );
  ```

  ```js JavaScript theme={"system"}
  const response = await client.setSettings({
    indexName: 'theIndexName',
    indexSettings: {
      ranking: ['desc(is_popular)', 'typo', 'geo', 'words', 'filters', 'proximity', 'attribute', 'exact', 'custom'],
    },
  });
  ```

  ```kotlin Kotlin theme={"system"}
  var response =
    client.setSettings(
      indexName = "INDEX_NAME",
      indexSettings =
        IndexSettings(
          ranking =
            listOf(
              "desc(is_popular)",
              "typo",
              "geo",
              "words",
              "filters",
              "proximity",
              "attribute",
              "exact",
              "custom",
            )
        ),
    )
  ```

  ```php PHP theme={"system"}
  $response = $client->setSettings(
      'INDEX_NAME',
      ['ranking' => [
          'desc(is_popular)',

          'typo',

          'geo',

          'words',

          'filters',

          'proximity',

          'attribute',

          'exact',

          'custom',
      ],
      ],
  );
  ```

  ```python Python theme={"system"}
  response = client.set_settings(
      index_name="INDEX_NAME",
      index_settings={
          "ranking": [
              "desc(is_popular)",
              "typo",
              "geo",
              "words",
              "filters",
              "proximity",
              "attribute",
              "exact",
              "custom",
          ],
      },
  )
  ```

  ```ruby Ruby theme={"system"}
  response = client.set_settings(
    "INDEX_NAME",
    Algolia::Search::IndexSettings.new(
      ranking: ["desc(is_popular)", "typo", "geo", "words", "filters", "proximity", "attribute", "exact", "custom"]
    )
  )
  ```

  ```scala Scala theme={"system"}
  val response = Await.result(
    client.setSettings(
      indexName = "INDEX_NAME",
      indexSettings = IndexSettings(
        ranking = Some(
          Seq("desc(is_popular)", "typo", "geo", "words", "filters", "proximity", "attribute", "exact", "custom")
        )
      )
    ),
    Duration(100, "sec")
  )
  ```

  ```swift Swift theme={"system"}
  let response = try await client.setSettings(
      indexName: "INDEX_NAME",
      indexSettings: IndexSettings(ranking: [
          "desc(is_popular)",
          "typo",
          "geo",
          "words",
          "filters",
          "proximity",
          "attribute",
          "exact",
          "custom",
      ])
  )
  ```
</CodeGroup>
