← Blog
Developer playbook · 16 min de lecture · Published avril 2026 · Author Serhat Dogan

Centre de migration SMS-Activate 2026 : Liste de contrôle pour les développeurs, mappage API et comparaison des remboursements

Centre de migration SMS-Activate 2026 : Liste de contrôle pour les développeurs, mappage API et comparaison des remboursements

If you are still carrying handler_api.php calls in your repo pointed at sms-activate.org, this is the hub you open at 9pm on a Friday and close with a working integration before midnight. Real endpoint mapping, real code diffs, refund policy comparison, and the gotchas that will bite you between the line you change and the alert that wakes you up on Monday morning.

Pourquoi ce guide existe

La plupart des articles « SMS-Activate est mort, voici une liste d'alternatives » omettent la partie qui consomme réellement les heures de développement : le code. S'inscrire auprès d'un nouveau fournisseur prend cinq minutes. Réécrire une intégration qui fonctionne discrètement en production depuis des années, avec des cas limites non testés et un suivi des coûts associé, prend plus de temps que vous ne le pensez.

Après avoir déployé notre couche de compatibilité en janvier 2026, nous avons commencé à recevoir les trois mêmes questions de chaque équipe qui l'a essayée :

  1. Quels points d'accès correspondent directement et lesquels nécessitent des modifications manuelles ?
  2. Comment maintenir mon suivi des coûts et mon flux de remboursement opérationnels sans réécriture ?
  3. Qu'est-ce qui casse silencieusement et se manifeste une semaine plus tard par une surprise de facturation ?

Ce guide répond à ces trois questions dans l'ordre et vous fournit du code à copier-coller que vous pouvez auditer avant de l'exécuter.

Le récapitulatif des 48 heures : ce qui s'est réellement passé

SMS-Activate a cessé de fonctionner le 29 décembre 2025. Il n'y a pas eu de bannière de maintenance programmée, d'outil de migration, ni d'avis public. Les utilisateurs tentant de se connecter tombaient sur une seule page indiquant que le service avait fermé définitivement. L'API renvoyait des réinitialisations de connexion sur tous les points d'accès en quelques heures.

Trois choses se sont produites rapidement :

Les conséquences se font encore sentir. En avril 2026, il y a des affaires actives devant les tribunaux des petites créances en Russie et au moins deux procès communautaires coordonnés tentant de récupérer les soldes gelés. Rien de tout cela n'aide votre code, c'est pourquoi nous allons nous concentrer sur la partie que vous pouvez réellement corriger.

Partie 1 : Carte de dépréciation de l'API

SMS-Activate proposait un seul point d'accès public à https://sms-activate.org/stubs/handler_api.php. Chaque action était un paramètre de chaîne de requête sur cette URL. Le tableau ci-dessous mappe chaque action majeure à son équivalent VerifySMS. La couche de compatibilité à https://api.verifysms.app/compat/handler_api.php accepte exactement la même forme de chaîne de requête.

Action SMS-ActivateObjectifCouche de compatibilité VerifySMSAPI native VerifySMS
getBalanceRetourne le solde USD sous forme de texteFonctionne sans modification. Retourne ACCESS_BALANCE:X.YYGET /v1/balance retourne du JSON
getNumbersStatusDisponibilité par paysFonctionne. Retourne le format de carte héritéGET /v1/countries/availability
getNumberLouer un numéro pour un serviceFonctionne. Retourne ACCESS_NUMBER:id:+phonePOST /v1/rentals
setStatusConfirmer ou annuler une locationFonctionne. Les codes d'état 1/3/6/8 se comportent de manière identiquePOST /v1/rentals/{id}/status
getStatusInterroger l'arrivée du SMSFonctionne. Retourne STATUS_WAIT_CODE, STATUS_OK:CODE, STATUS_WAIT_RETRYGET /v1/rentals/{id}
getPricesRécupérer la table des prixFonctionne. Retourne la tarification VerifySMS dans le format JSON héritéGET /v1/prices
getCountriesCarte des codes paysFonctionne. Retourne les identifiants numériques hérités et les codes ISO-3166GET /v1/countries
getTopCountriesByServiceMeilleurs pays par serviceRetourne les données VerifySMS en temps réel au lieu des classements SMS-Activate mis en cacheGET /v1/services/{id}/top-countries

Une poignée d'actions SMS-Activate moins utilisées ne correspondent pas directement. getRentServicesAndCountries et l'API de location longue durée étaient spécifiques à SMS-Activate et n'ont pas de couche de compatibilité. Si votre intégration utilisait ces fonctions, vous devriez passer au point d'accès longue durée natif de VerifySMS à POST /v1/rentals/long, qui est documenté séparément.

Partie 2 : Tutoriels de migration de code

Les extraits suivants sont exactement tels que je les ai testés dans notre propre environnement de staging en janvier. Je les ai délibérément gardés simples pour que vous puissiez les lire par rapport à votre propre code sans changer de contexte.

Python (requests)

Le seul changement requis est l'URL de base. Si vous avez déjà encapsulé l'API dans un petit module client, la différence est une seule ligne.

import os
import requests

# AVANT
# BASE_URL = "https://sms-activate.org/stubs/handler_api.php"
# APRÈS
BASE_URL = "https://api.verifysms.app/compat/handler_api.php"
API_KEY = os.environ["SMS_API_KEY"]

def get_number(service: str, country: int) -> tuple[str, str]:
    resp = requests.get(BASE_URL, params={
        "api_key": API_KEY,
        "action": "getNumber",
        "service": service,
        "country": country,
    }, timeout=30)
    resp.raise_for_status()
    # ACCESS_NUMBER:12345:+441234567890
    status, rental_id, phone = resp.text.split(":", 2)
    if status != "ACCESS_NUMBER":
        raise RuntimeError(f"unexpected response: {resp.text}")
    return rental_id, phone

def wait_for_code(rental_id: str, deadline_seconds: int = 180) -> str:
    import time
    start = time.monotonic()
    while time.monotonic() - start < deadline_seconds:
        resp = requests.get(BASE_URL, params={
            "api_key": API_KEY,
            "action": "getStatus",
            "id": rental_id,
        }, timeout=15).text
        if resp.startswith("STATUS_OK:"):
            return resp.split(":", 1)[1]
        time.sleep(4)
    # Marquer comme inutilisé pour obtenir le remboursement
    requests.get(BASE_URL, params={
        "api_key": API_KEY,
        "action": "setStatus",
        "status": 8,
        "id": rental_id,
    }, timeout=15)
    raise TimeoutError(f"no code after {deadline_seconds}s")

Node.js (axios)

import axios from "axios";

// AVANT
// const BASE_URL = "https://sms-activate.org/stubs/handler_api.php";
// APRÈS
const BASE_URL = "https://api.verifysms.app/compat/handler_api.php";
const API_KEY = process.env.SMS_API_KEY;

export async function getNumber(service, country) {
  const { data } = await axios.get(BASE_URL, {
    params: { api_key: API_KEY, action: "getNumber", service, country },
    timeout: 30_000,
  });
  const [status, rentalId, phone] = data.split(":");
  if (status !== "ACCESS_NUMBER") {
    throw new Error(`unexpected response: ${data}`);
  }
  return { rentalId, phone };
}

export async function waitForCode(rentalId, deadlineMs = 180_000) {
  const start = Date.now();
  while (Date.now() - start < deadlineMs) {
    const { data } = await axios.get(BASE_URL, {
      params: { api_key: API_KEY, action: "getStatus", id: rentalId },
      timeout: 15_000,
    });
    if (data.startsWith("STATUS_OK:")) return data.split(":")[1];
    await new Promise((r) => setTimeout(r, 4000));
  }
  await axios.get(BASE_URL, {
    params: { api_key: API_KEY, action: "setStatus", status: 8, id: rentalId },
    timeout: 15_000,
  });
  throw new Error(`no code after ${deadlineMs}ms`);
}

PHP (curl)

<?php
// AVANT
// const BASE_URL = "https://sms-activate.org/stubs/handler_api.php";
// APRÈS
const BASE_URL = "https://api.verifysms.app/compat/handler_api.php";

function sms_call(array $params): string {
    $params["api_key"] = getenv("SMS_API_KEY");
    $url = BASE_URL . "?" . http_build_query($params);
    $ch  = curl_init($url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_TIMEOUT, 30);
    $body = curl_exec($ch);
    curl_close($ch);
    return $body;
}

function get_number(string $service, int $country): array {
    $resp = sms_call(["action" => "getNumber", "service" => $service, "country" => $country]);
    [$status, $id, $phone] = explode(":", $resp, 3);
    if ($status !== "ACCESS_NUMBER") {
        throw new RuntimeException("unexpected: $resp");
    }
    return ["id" => $id, "phone" => $phone];
}

Go (net/http)

package sms

import (
    "errors"
    "fmt"
    "io"
    "net/http"
    "net/url"
    "os"
    "strings"
    "time"
)

// AVANT
// const baseURL = "https://sms-activate.org/stubs/handler_api.php"
// APRÈS
const baseURL = "https://api.verifysms.app/compat/handler_api.php"

func call(params url.Values) (string, error) {
    params.Set("api_key", os.Getenv("SMS_API_KEY"))
    req, _ := http.NewRequest("GET", baseURL+"?"+params.Encode(), nil)
    client := &http.Client{Timeout: 30 * time.Second}
    resp, err := client.Do(req)
    if err != nil {
        return "", err
    }
    defer resp.Body.Close()
    body, _ := io.ReadAll(resp.Body)
    return string(body), nil
}

func GetNumber(service string, country int) (id, phone string, err error) {
    body, err := call(url.Values{
        "action":  {"getNumber"},
        "service": {service},
        "country": {fmt.Sprint(country)},
    })
    if err != nil {
        return "", "", err
    }
    parts := strings.SplitN(body, ":", 3)
    if len(parts) != 3 || parts[0] != "ACCESS_NUMBER" {
        return "", "", errors.New("unexpected: " + body)
    }
    return parts[1], parts[2], nil
}

L'exemple Go est délibérément écrit sans aucune bibliothèque tierce pour que vous puissiez l'intégrer dans un service minimal sans ajouter de dépendances. Le même schéma s'applique dans toutes les autres langues : changez l'URL, gardez le reste, et laissez votre gestion d'erreurs existante prendre le relais.

Partie 3 : Pièges qui vous feront trébucher

Voici les endroits où la couche de compatibilité est fidèle aux particularités de SMS-Activate, mais où ces particularités elles-mêmes vous surprendront si vous n'avez pas touché à ce code depuis un moment.

Les identifiants de pays ne sont pas des codes ISO

SMS-Activate numéroté les pays dans son propre ordre : la Russie était 0, les États-Unis étaient 187, l'Indonésie était 6, et ainsi de suite. Si votre intégration a ces nombres magiques codés en dur, ils fonctionnent toujours sur la couche de compatibilité. Si vous écrivez du nouveau code, privilégiez la forme alpha-2 ISO-3166 (RU, US, ID) que la couche de compatibilité accepte également. Ne mélangez pas les deux styles sur le même site d'appel, car le débogage futur sera douloureux.

Code d'état 3 contre 6

Le code d'action 3 de setStatus signifiait « demander un autre SMS » dans le monde de SMS-Activate, et le code 6 signifiait « accepter le code comme valide ». Ces deux codes sont faciles à intervertir à la hâte et ils ont des résultats de facturation opposés : 3 vous maintient facturé, 6 confirme la vérification réussie. La couche de compatibilité se comporte de la même manière. Recherchez dans votre code setStatus et assurez-vous que la branche qui prend le code 6 ne s'exécute qu'après avoir été certain que la vérification a réussi.

Délais d'attente et disjoncteurs

SMS-Activate, sous charge, renvoyait parfois un 200 avec un corps vide au lieu d'une erreur HTTP. Les clients défensifs encapsulent l'appel dans un délai d'attente et traitent le corps vide comme un signal de nouvelle tentative. VerifySMS ne renvoie jamais de corps vide sur la couche de compatibilité. Si votre client traite toujours le corps vide comme une nouvelle tentative, il consommera du budget sur un réseau instable car la nouvelle tentative atteindra un identifiant de location différent. Le modèle le plus sûr est de vérifier les préfixes de réponse connus (ACCESS_, STATUS_, BAD_) et de traiter tout le reste comme un échec définitif, pas comme un échec transitoire.

Les limites de débit passent de par clé à par IP

Les limites de débit de SMS-Activate étaient indexées sur le jeton API. Les limites de débit de VerifySMS sont indexées sur la combinaison du jeton API et de l'adresse IP source, car nous constatons beaucoup d'abus de scripts de scraping partageant une seule clé sur un botnet. Pour le trafic de production normal provenant d'un seul serveur ou d'un pool équilibré, cela est invisible. Si vous exécutez des tâches CI distribuées qui partagent toutes une clé de staging, vous pourriez voir un 429 la première fois que toute la flotte démarre en même temps. La solution consiste à laisser le canary s'exécuter à partir d'un nœud pendant une journée avant de se propager.

Le moment des remboursements semble instantané car il est réellement instantané

Ce n'est pas tant un piège qu'une agréable surprise. Alors que les remboursements SMS-Activate prenaient quelques heures pour apparaître sur votre solde, les remboursements VerifySMS apparaissent en moins de 60 secondes. Si votre suivi des coûts lit le solde selon un calendrier, il enregistrera le remboursement comme un crédit que l'ancien système aurait manqué. Les tableaux de bord de rapprochement signalent parfois cela comme une anomalie le premier jour.

Partie 4 : Comparaison des prix, réalité

Avant la fermeture, SMS-Activate était le plancher du marché. Les numéros russes coûtaient entre 0,03 $ et 0,05 $ par vérification, et les acheteurs en gros payaient encore moins. Ce plancher a disparu. Voici où se situent les fournisseurs restants en avril 2026 pour les services les plus courants, extraits de la page de tarification publique de chaque fournisseur à la date du :

Service5simTextVerifiedSMSPVASMS-MANVerifySMS
WhatsApp / Russie0,014 $0,05 $0,035 $0,10 $
WhatsApp / États-Unis0,27 $0,25 $0,28 $0,22 $0,18 $
Telegram / Russie0,016 $0,05 $0,04 $0,10 $
Telegram / États-Unis0,35 $0,40 $0,38 $0,30 $0,20 $
Google / Indonésie0,07 $0,08 $0,06 $0,10 $

Le schéma est simple : 5sim et SMS-MAN gagnent sur les prix russes bas, TextVerified est le niveau premium américain, et VerifySMS se situe au milieu avec une base de 0,10 $ pour tout sauf les numéros non VoIP américains les plus chers. Si votre budget était ajusté aux prix planchers de SMS-Activate, attendez-vous à payer deux à cinq fois plus par vérification, quel que soit le remplacement que vous choisissez.

Deux notes sur ce tableau. Premièrement, chaque fournisseur (VerifySMS inclus) augmente et diminue les prix individuels des pays en réponse aux coûts des opérateurs, alors confirmez le prix actuel dans votre propre tableau de bord avant de vous engager un budget. Deuxièmement, le prix *effectif* par vérification réussie dépend du ratio de remboursement. Un fournisseur avec un prix affiché de 0,08 $ et un taux de réussite de 70 % vous coûte plus cher par succès qu'un fournisseur à 0,10 $ avec des remboursements automatiques et un taux de réussite de 90 %.

Partie 5 : La checklist de migration en 10 étapes

C'est la séquence réelle que nous avons fait suivre à nos propres utilisateurs en janvier. Elle suppose un seul développeur avec accès au dépôt, un service de production et un environnement de staging. Augmentez les pourcentages de canary si vous exécutez plusieurs services ou un monorepo.

  1. Inventoriez chaque site d'appel. Exécutez git grep -n 'sms-activate\.org\|handler_api\.php\|getNumber\|setStatus' et listez tous les fichiers qui accèdent à l'ancienne API. Si vous en trouvez plus d'une douzaine, choisissez un module wrapper et centralisez les appels d'abord avant de migrer.
  2. Obtenez une clé API VerifySMS. Inscrivez-vous, ajoutez un petit solde, et générez une clé limitée pour le staging. Ne mettez pas la clé de production dans le dépôt.
  3. Modifiez l'URL de base. Remplacez l'hôte SMS-Activate par api.verifysms.app/compat/handler_api.php. Ne modifiez pas la chaîne de requête. Validez cela seul pour que la différence soit nette.
  4. Exécutez vos tests existants. Si les tests accèdent à la vraie API, pointez-les vers le staging et surveillez les décalages de format. S'ils simulent l'API, exécutez-les également contre le point d'accès staging en direct afin de détecter les dérives de contrat.
  5. Reconfirmez les identifiants de pays. Parcourez votre code à la recherche de constantes de pays. Si vous utilisez les identifiants numériques hérités, ils fonctionnent toujours. Si vous en avez l'occasion, remplacez-les par des codes ISO-3166 car le prochain développeur à toucher ce fichier vous remerciera.
  6. Configurez les demandes de remboursement. Confirmez que votre chemin de délai d'attente appelle setStatus avec le statut 8. Sans cela, vous obtiendrez toujours des remboursements (nous remboursons automatiquement les locations expirées) mais votre suivi des coûts sera en retard par rapport à la réalité.
  7. Mettez à jour votre suivi des coûts. Lisez le coût à partir de l'en-tête de réponse X-VerifySMS-Cost au lieu de l'analyser à partir de la table des prix. Ce seul changement rend votre tableau de bord financier précis au centime près.
  8. Surveillance. Ajoutez des alertes sur le taux de réussite, la latence p95 et le ratio de remboursement par rapport à votre référence existante. Choisissez des seuils que vous pouvez défendre, pas ceux que vous pensez être « suffisants ».
  9. Canary 5 % pendant 24 heures. Routez une petite partie du trafic de production via le nouveau point d'accès. Surveillez le tableau de bord, pas seulement les alertes.
  10. Passez le reste. Une fois la fenêtre de canary terminée, déplacez les 95 % restants et laissez le code client existant commenté (pas supprimé) pendant un cycle de publication afin d'avoir un retour arrière rapide.

Supprimez l'ancien code lors de la prochaine publication. Ne laissez pas de sites d'appel morts traîner plus d'une semaine, car la personne suivante qui touchera le module les copiera accidentellement dans une nouvelle intégration.

Questions fréquemment posées

La couche de compatibilité SMS-Activate est-elle une vraie API ou juste un stub ?

C'est un point d'accès réel à api.verifysms.app/compat/handler_api.php qui accepte toutes les actions majeures de la documentation publique de SMS-Activate : getBalance, getNumber, getStatus, setStatus, getPrices et getCountries. Les requêtes sont redirigées vers notre API native en coulisses, vous bénéficiez donc de la tarification, de la couverture et du comportement de remboursement de VerifySMS sans aucune modification de code de votre côté.

Ma vieille clé API fonctionnera-t-elle ?

Non. Les clés API SMS-Activate ont cessé de s'authentifier le jour où le service a fermé. Vous avez besoin d'une nouvelle clé de VerifySMS. Inscrivez-vous, ajoutez un petit solde et générez une clé depuis le tableau de bord. Le format de la clé est identique en longueur, vous pouvez donc la coller dans la même variable d'environnement.

Comment fonctionnent les remboursements par rapport à SMS-Activate ?

SMS-Activate exigeait que vous appeliez setStatus avec le code d'état 8 dans les 20 minutes pour marquer un numéro comme inutilisé, et les remboursements étaient traités manuellement en quelques heures. VerifySMS accepte le même appel setStatus et rembourse le montant total sur votre solde en moins de 60 secondes. Si vous oubliez d'appeler setStatus, notre système rembourse automatiquement tout numéro qui n'a jamais reçu de SMS après l'expiration de la fenêtre de location.

Quels pays sont pris en charge ?

VerifySMS couvre plus de 200 pays. Tous les pays proposés par SMS-Activate sont disponibles sur VerifySMS, y compris la Russie, l'Indonésie, le Vietnam, le Nigeria et les États-Unis. Vous pouvez conserver votre mappage d'identifiants de pays existant ou migrer vers des codes alpha-2 ISO-3166 quand vous le souhaitez.

Le prix est-il le même ?

Non. Les prix planchers de SMS-Activate de 0,03 $ à 0,05 $ par vérification pour les numéros russes ont disparu du marché ouvert. Les prix actuels du marché varient de 0,10 $ pour les services courants à 0,25 $ pour les numéros non VoIP américains sur des plateformes plus strictes. VerifySMS facture 0,10 $ comme base et publie les prix par pays sur le tableau de bord.

Dois-je modifier ma logique de sondage ?

Non. L'appel getStatus renvoie STATUS_WAIT_CODE et STATUS_OK dans le même format que celui utilisé par SMS-Activate. Les intervalles de sondage de 3 à 5 secondes fonctionnent toujours. Le seul nouveau comportement est que VerifySMS expose également une URL de webhook dans le tableau de bord, vous pouvez donc arrêter complètement le sondage si vous préférez un flux piloté par événements.

Que se passe-t-il si la couche de compatibilité est un jour dépréciée ?

La couche de compatibilité est considérée comme une interface publique permanente. Si nous changeons un jour son comportement, nous publierons une fenêtre de dépréciation d'au moins six mois avec une note de migration complète. L'API JSON native de VerifySMS est également documentée, vous pouvez donc migrer hors de la couche de compatibilité à votre rythme, quand cela vous semble approprié.

Comment tester sans dépenser d'argent ?

Le tableau de bord VerifySMS expose un mode sandbox qui renvoie des numéros de téléphone simulés et des codes SMS prédéfinis sans débiter votre solde. Activez le drapeau sandbox dans le tableau de bord ou envoyez l'en-tête X-Sandbox-Mode avec toute requête pour tester vos chemins de code avant de passer en production.

Puis-je migrer depuis d'autres services aussi ?

Oui. Ce guide est écrit autour de l'API SMS-Activate car c'est là que se trouve la plupart du code bloqué, mais la même checklist s'applique aux migrations depuis 5sim, SMS-MAN, ou tout autre service compatible avec handler_api. La couche de compatibilité reconnaît les paramètres handler_api.php quel que soit le service que vous appeliez auparavant.

Combien de temps prend une migration réelle ?

Pour une intégration de service unique avec quelques dizaines de sites d'appel, prévoyez deux à quatre heures de travail concentré, plus une fenêtre de canary de 24 heures avant de basculer tout le trafic. Les migrations multi-services plus importantes avec gestion d'erreurs personnalisée, analyses et nouvelles tentatives peuvent prendre plus de temps, mais se terminent généralement dans la journée de travail.

Perds-je mes données historiques ?

L'historique des vérifications SMS-Activate a été mis hors ligne lorsque le service a fermé et n'est pas récupérable. VerifySMS conserve un journal d'audit complet de chaque tentative de vérification sur votre compte pendant 12 mois, accessible depuis le tableau de bord et via l'extension /compat/handler_api.php?action=getHistory.

Cela affecte-t-il ma posture GDPR ou de conformité ?

VerifySMS est enregistré au Royaume-Uni et suit le RGPD britannique. Nous publions notre politique de conservation des données, nos sous-traitants et notre DPA sur la page de confidentialité. Si votre configuration précédente nécessitait une DPA avec SMS-Activate, contactez-nous et nous contresignerons le même accord dans un délai d'un jour ouvrable.

Prochaines étapes

Si vous avez lu jusqu'ici, vous avez déjà les éléments dont vous avez besoin. Commencez par l'étape d'inventaire, faites examiner le changement d'URL de base par un collègue, et exécutez le canary pendant la nuit. Le guide est volontairement court ; la partie difficile est la discipline de s'arrêter après la fenêtre de canary au lieu de tout basculer en une seule validation.

Lecture connexe sur le reste du site :

Prêt à réaliser la migration en une seule soirée ?

Créer une clé API VerifySMS →

Mode Sandbox inclus · Garantie de remboursement automatique · 200+ pays · Couche de compatibilité SMS-Activate sur /compat/handler_api.php

Next steps

If you have read this far, you already have the pieces you need. Start with the inventory step, get the base URL swap in front of a teammate for review, and run the canary overnight. The playbook is small on purpose; the hard part is the discipline to stop after the canary window instead of cutting everything over in one commit.

Related reading on the rest of the site:

Ready to cut the migration to a single evening?

Create a VerifySMS API key →

Sandbox mode included · Auto-refund guarantee · 200+ countries · SMS-Activate compat layer on /compat/handler_api.php