Farewell #Skype. Here’s how their #API worked.
So, with the shutdown of Skype in May 2025, only two months away, there is not much need to hold on tight to our source code for the Skype API. It worked well for us for years on AvatarAPI.com
but with the imminent shutdown, their API will undoubtedly stop working as soon as Skype is shut down, and will no longer be relevant, even if the API stays active for a little while later.
In this post, we’ll take a deep dive into a C# implementation of a Skype user search feature using HTTP requests. This code interacts with Skype’s search API to fetch user profiles based on a given search parameter. We’ll break down the core functionality, security considerations, and potential improvements.
Overview of the SkypeSearch Class
The SkypeSearch class provides a static method, Search, which sends a request to Skype’s search API to retrieve user profiles. It uses an authentication token (SkypeToken) and manages retries in case of failures. Let’s explore its components in detail.
Key Features of the Implementation
- Handles API Requests Securely: The method sets various security protocols (
Ssl3,Tls,Tls11,Tls12) to ensure compatibility with Skype’s API. - Custom Headers for Authentication: It constructs an HTTP request with necessary headers, including
x-skypetoken,x-skype-client, and others. - Manages Rate Limits & Token Refresh: If the API responds with an empty result (potentially due to a
429 Too Many Requestserror), the token is refreshed, and the search is retried up to five times. - Enhances API Response: The method modifies the API response to include an additional
avatarImageUrlfield for each result.
Breaking Down the Search Method
Constructing the API Request
var requestNumber = new Random().Next(100000, 999999);
var url = string.Format(
"https://search.skype.com/v2.0/search?searchString={0}&requestId={1}&locale=en-GB&sessionId={2}",
searchParameter, requestNumber, Guid.NewGuid());
This snippet constructs the API request URL with dynamic query parameters, including:
searchString: The user input for searching Skype profiles.requestId: A randomly generated request ID for uniqueness.sessionId: A newly generated GUID for session tracking.
Setting HTTP Headers
HTTPHeaderHandler wicket = nvc =>
{
var nvcSArgs = new NameValueCollection
{
{"x-skypetoken", token.Value},
{"x-skype-client", "1418/8.134.0.202"},
{"Origin", "https://web.skype.com"}
};
return nvcSArgs;
};
Here, we define essential request headers for authentication and compatibility. The x-skypetoken is a crucial element, as it ensures access to Skype’s search API.
Handling API Responses & Retrying on Failure
if (jsonResponse == "")
{
token = new SkypeToken();
return Search(searchParameter, token, ++maxRecursion);
}
If an empty response is received (potentially due to an API rate limit), the method refreshes the authentication token and retries the request up to five times to prevent excessive loops.
Enhancing API Response with Profile Avatars
foreach (var node in jResponse["results"])
{
var skypeId = node["nodeProfileData"]["skypeId"] + "";
var avatarImageUrl = string.Format(
"https://avatar.skype.com/v1/avatars/{0}/public?size=l",
skypeId);
node["nodeProfileData"]["avatarImageUrl"] = avatarImageUrl;
}
After receiving the API response, the code iterates through the user results and appends an avatarImageUrl field using Skype’s avatar service.
using System;
using System.Collections.Specialized;
using System.Net;
using System.Text;
using Newtonsoft.Json.Linq;
namespace SkypeGraph
{
public class SkypeSearch
{
public static JObject Search(string searchParameter, SkypeToken token, int maxRecursion = 0)
{
if (maxRecursion == 5) throw new Exception("Preventing excessive retries");
ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 |
SecurityProtocolType.Tls |
SecurityProtocolType.Tls11 |
SecurityProtocolType.Tls12;
var requestNumber = new Random().Next(100000, 999999);
var url = string.Format("https://search.skype.com/v2.0/search?searchString={0}&requestId={1}&locale=en-GB&sessionId={2}", searchParameter, requestNumber, Guid.NewGuid());
var http = new HTTPRequest {Encoder = Encoding.UTF8};
HTTPHeaderHandler wicket = nvc =>
{
var nvcSArgs = new NameValueCollection
{
{"x-skypetoken", token.Value},
{"x-skypegraphservicesettings", ""},
{"x-skype-client","1418/8.134.0.202"},
{"x-ecs-etag", "GAx0SLim69RWpjmJ9Dpc4QBHAou0pY//fX4AZ9JVKU4="},
{"Origin", "https://web.skype.com"}
};
return nvcSArgs;
};
http.OverrideUserAgent =
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36";
http.OverrideAccept = "application/json";
http.TimeOut = TimeSpan.FromSeconds(5);
http.HeaderHandler = wicket;
http.ContentType = "application/json";
http.Referer = "https://web.skype.com/";
var jsonResponse = http.Request(url);
if (jsonResponse == "")
{
// In case of a 429 (Too many requests), then refresh the token.
token = new SkypeToken();
return Search(searchParameter, token, ++maxRecursion);
}
var jResponse = JObject.Parse(jsonResponse);
#region sample
/*
{
"requestId":"240120",
"results":[
{
"nodeProfileData":{
"skypeId":"live:octavioaparicio_jr",
"skypeHandle":"live:octavioaparicio_jr",
"name":"octavio aparicio",
"avatarUrl":"https://api.skype.com/users/live:octavioaparicio_jr/profile/avatar",
"country":"Mexico",
"countryCode":"mx",
"contactType":"Skype4Consumer"
}
}
]
}
*/
#endregion
foreach (var node in jResponse["results"])
{
var skypeId = node["nodeProfileData"]["skypeId"] + "";
var avatarImageUrl = string.Format("https://avatar.skype.com/v1/avatars/{0}/public?size=l", skypeId);
node["nodeProfileData"]["avatarImageUrl"] = avatarImageUrl;
}
return jResponse;
}
}
}
As of 9/5/2025 – this API is still functional, even though the Skype App is now down.
LikeLike