🚫 Why AWS SDK for S3 No Longer Works Smoothly with .NET Framework 4.8 β€” and How to Fix It

In 2024, more .NET developers are finding themselves in a strange situation: suddenly, tried-and-tested .NET Framework 4.8 applications that interact with Amazon S3 start throwing cryptic build errors or runtime exceptions. The culprit? The AWS SDK for .NET has increasingly shifted toward support for .NET Core / .NET 6+, and full compatibility with .NET Framework is eroding.

In this post, we’ll explain:

  • Why this happens
  • What errors you might see
  • And how to remove the AWS SDK altogether and replace it with pure .NET 4.8-compatible code for downloading (and uploading) files from S3 using Signature Version 4.

🧨 The Problem: AWS SDK & .NET Framework 4.8

The AWS SDK for .NET (like AWSSDK.S3) now depends on modern libraries like:

  • System.Text.Json
  • System.Buffers
  • System.Runtime.CompilerServices.Unsafe
  • Microsoft.Bcl.AsyncInterfaces

These dependencies were designed for .NET Core and later versions β€” not .NET Framework. While it was once possible to work around this with binding redirects and careful version pinning, the situation has become unstable and error-prone.


❗ Common Symptoms

You may see errors like:

Could not load file or assembly ‘System.Text.Json, Version=6.0.0.11’

Or:

Could not load file or assembly ‘System.Buffers, Version=4.0.5.0’

Or during build:

Warning: Unable to update auto-refresh reference ‘system.text.json.dll’

Even if you install the correct packages, you may end up needing to fight bindingRedirect hell, and still not get a working application.


βœ… The Solution: Remove the AWS SDK

Fortunately, you don’t need the SDK to use S3. All AWS S3 requires is a properly signed HTTP request using AWS Signature Version 4, and you can create that yourself using standard .NET 4.8 libraries.


πŸ” Downloading from S3 Without the AWS SDK

Here’s how you can download a file from S3 using HttpWebRequest and Signature Version 4.

βœ”οΈ The Key Points:

  • You must include the x-amz-content-sha256 header (even for GETs!)
  • You sign the request using your AWS secret key
  • No external packages required β€” works on plain .NET 4.8

🧩 Code Snippet


public static byte[] DownloadFromS3(string bucketName, string objectKey, string region, string accessKey, string secretKey)
{
var method = "GET";
var service = "s3";
var host = $"{bucketName}.s3.{region}.amazonaws.com";
var uri = $"https://{host}/{objectKey}";
var requestDate = DateTime.UtcNow;
var amzDate = requestDate.ToString("yyyyMMddTHHmmssZ");
var dateStamp = requestDate.ToString("yyyyMMdd");
var canonicalUri = "/" + objectKey;
var signedHeaders = "host;x-amz-content-sha256;x-amz-date";
var payloadHash = HashSHA256(string.Empty); // Required even for GET

var canonicalRequest = $"{method}\n{canonicalUri}\n\nhost:{host}\nx-amz-content-sha256:{payloadHash}\nx-amz-date:{amzDate}\n\n{signedHeaders}\n{payloadHash}";
var credentialScope = $"{dateStamp}/{region}/{service}/aws4_request";
var stringToSign = $"AWS4-HMAC-SHA256\n{amzDate}\n{credentialScope}\n{HashSHA256(canonicalRequest)}";

var signingKey = GetSignatureKey(secretKey, dateStamp, region, service);
var signature = ToHexString(HmacSHA256(signingKey, stringToSign));

var authorizationHeader = $"AWS4-HMAC-SHA256 Credential={accessKey}/{credentialScope}, SignedHeaders={signedHeaders}, Signature={signature}";

var request = (HttpWebRequest)WebRequest.Create(uri);
request.Method = method;
request.Headers["Authorization"] = authorizationHeader;
request.Headers["x-amz-date"] = amzDate;
request.Headers["x-amz-content-sha256"] = payloadHash;

try
{
    using (var response = (HttpWebResponse)request.GetResponse())
    using (var responseStream = response.GetResponseStream())
    using (var memoryStream = new MemoryStream())
    {
        responseStream.CopyTo(memoryStream);
        return memoryStream.ToArray();
    }
}
catch (WebException ex)
{
    using (var errorResponse = (HttpWebResponse)ex.Response)
    using (var reader = new StreamReader(errorResponse.GetResponseStream()))
    {
        var errorText = reader.ReadToEnd();
        throw new Exception($"S3 request failed: {errorText}", ex);
    }
}

}
πŸ”§ Supporting Methods


private static string HashSHA256(string text)
{
using (var sha256 = SHA256.Create())
{
return ToHexString(sha256.ComputeHash(Encoding.UTF8.GetBytes(text)));
}
}
private static byte[] HmacSHA256(byte[] key, string data)
{
using (var hmac = new HMACSHA256(key))
{
return hmac.ComputeHash(Encoding.UTF8.GetBytes(data));
}
}
private static byte[] GetSignatureKey(string secretKey, string dateStamp, string region, string service)
{
var kSecret = Encoding.UTF8.GetBytes("AWS4" + secretKey);
var kDate = HmacSHA256(kSecret, dateStamp);
var kRegion = HmacSHA256(kDate, region);
var kService = HmacSHA256(kRegion, service);
return HmacSHA256(kService, "aws4_request");
}
private static string ToHexString(byte[] bytes)
{
return BitConverter.ToString(bytes).Replace("-", "").ToLowerInvariant();
}


πŸ“ Uploading to S3 Without the AWS SDK
You can extend the same technique for PUT requests. The only differences are:

You calculate the SHA-256 hash of the file content

You include a Content-Type and Content-Length header

You use PUT instead of GET

Let me know in the comments if you’d like the full upload version β€” it follows the same Signature V4 pattern.

βœ… Summary
Feature AWS SDK for .NET Manual Signature V4
.NET Framework 4.8 support ❌ Increasingly broken βœ… Fully supported
Heavy NuGet dependencies βœ… ❌ Minimal
Simple download/upload βœ… βœ… (with more code)
Presigned URLs βœ… 🟑 Manual support

Final Thoughts
If you’re stuck on .NET Framework 4.8 and running into weird AWS SDK issues β€” you’re not alone. But you’re not stuck either. Dropping the SDK and using HTTP + Signature V4 is entirely viable, especially for simple tasks like uploading/downloading S3 files.

Let me know if you’d like a full upload example, presigned URL generator, or if you’re considering migrating to .NET 6+.

πŸ” Fetching #Flickr Profile Information by #Email Using AvatarAPI

As developers, integrating rich user profile data can elevate the experience of our apps, websites, and services. Today, let’s dive into how you can fetch Flickr profile information by email using AvatarAPI, a handy service that consolidates avatar and profile data from multiple platforms β€” including Flickr.


Why Flickr?

Flickr remains a popular photo-sharing platform with millions of users showcasing their photography. While many apps focus on social media giants like Twitter or LinkedIn, Flickr’s unique focus on imagery and photography makes it valuable for projects centered around visual content or creative communities.


What is AvatarAPI?

AvatarAPI is an API designed to fetch user profile avatars and metadata by providing common identifiers such as emails, usernames, or social media IDs. It supports many sources, including Flickr, making it a one-stop shop for profile enrichment.


How to Fetch Flickr Profile Data via AvatarAPI

When you request Flickr profile info through AvatarAPI, the data is returned in XML format, containing a rich set of fields to help you understand the user behind the email address.

Here’s an example snippet of the raw XML you get from AvatarAPI for a Flickr profile:

xmlCopyEdit<?xml version="1.0" encoding="utf-8" ?>
<rsp stat="ok">
  <person id="12345678@N04" nsid="12345678@N04" ispro="0" is_deleted="0" iconserver="2257" iconfarm="3" path_alias="random_alias" has_stats="0" has_adfree="0" has_free_standard_shipping="0" has_free_educational_resources="0">
    <username>Dr. Random</username>
    <realname>Random Name</realname>
    <location />
    <timezone label="Random City, Random State" offset="+00:00" timezone_id="Europe/London" timezone="57" />
    <description>Random Description</description>
    <photosurl>https://www.flickr.com/photos/random_alias/</photosurl>
    <profileurl>https://www.flickr.com/people/random_alias/</profileurl>
    <mobileurl>https://m.flickr.com/photostream.gne?id=12345678</mobileurl>
    <photos>
      <firstdatetaken>2000-01-01 00:00:00</firstdatetaken>
      <firstdate>946684800</firstdate>
      <count>10</count>
    </photos>
  </person>
</rsp>

Breaking Down the XML

  • person attributes:
    • id and nsid: The unique Flickr user ID.
    • ispro: Whether the user has a pro account (1 for yes, 0 for no).
    • is_deleted: Indicates if the account is deleted.
    • iconserver and iconfarm: Used to construct the user’s avatar image URL.
    • path_alias: The user’s Flickr alias, used in URLs.
  • Child elements:
    • <username>: The Flickr username.
    • <realname>: The user’s real name, if provided.
    • <location>: Location info, may be empty.
    • <timezone>: Detailed timezone information with label and offset.
    • <description>: User’s personal description or bio.
    • <photosurl>, <profileurl>, <mobileurl>: URLs to various Flickr profile pages.
    • <photos>: Info about user’s photos:
      • <firstdatetaken>: When the user took their first photo.
      • <count>: Number of photos uploaded.

How to Use This Data

With this data, you can:

  • Display user avatars and profile links inside your app or website.
  • Show contextual user info like location and bio.
  • Highlight photography activity through photo counts and dates.
  • Personalize user experiences by leveraging timezone and locale info.

Constructing the Avatar URL

Flickr stores user icons using a combination of the iconfarm, iconserver, and nsid. The avatar URL can be built as:

bashCopyEdithttps://farm{iconfarm}.staticflickr.com/{iconserver}/buddyicons/{nsid}.jpg

For example, with iconfarm=3, iconserver=2257, and nsid=12345678@N04:

bashCopyEdithttps://farm3.staticflickr.com/2257/buddyicons/12345678@N04.jpg

Wrapping Up

Thanks to AvatarAPI’s support for Flickr, accessing rich profile data by just knowing a user’s email has never been easier. Whether you’re building a photography community platform, enhancing contact profiles, or adding social proof, the Flickr profile data returned by AvatarAPI can help bring your users’ identities to life.

You can explore the full AvatarAPI docs for Flickr and other providers here: https://docs.avatarapi.com/.


If you want, I can help you write example code snippets in your favorite programming language to get started quickly with AvatarAPI and Flickr!


Happy coding!
β€” Your Friendly .NET Dev Blog

Categories: Uncategorized

Scam warning: Shaw Holdings Pte ltd

Scam warning!

TL; DR;
If you get approached by this company for investment. It’s a scam. Evidence below
https://drive.google.com/drive/folders/1s8PmzNv9YZQI_C50Tx118cqO9REkJgvu?usp=sharing

I got approached by this company, which in the exchange of 2 emails offered a $1M investment. To good to be true. With zero-due diligence. I’ve been trhough similar processes before, and expect a month’s worth of due dilligence document preparation before getting to the point of an offer.

Anyway, so I did due dilligence on them, and specifically, their director:
NRIC/FIN/Passport No. : S8838988C
Name : CHENG YONG TECK
Nationality/Citizenship : SINGAPORE CITIZEN
Address : 10 MARTIN PLACE, #17-14, MARTIN MODERN, SINGAPORE 237963

The director being involved in a bizarre mix of different companies, including immigration (Β linkΒ ) , weddings, plastic surgery (Β linkΒ )Β  , Weight loss, and engineering companies.

Β Specifically, the Plastic surgery business has left lots of people our of pocket, and is connected to the other holding company;Β  quote “Anyway, both Dore Aesthethics and Dore Clinic are owned by Kate Holdings.The Director is Cheng Yong Teck who lives in Orchard Residences. Born in 1988.”

Categories: Uncategorized

πŸ” Fetching Adobe Profile Information by Email Using AvatarAPI

In the age of personalization and integration, being able to retrieve user profile images and basic information programmatically can be invaluable for CRMs, marketing platforms, and analytics systems. AvatarAPI provides a simple yet powerful way to fetch profile data from various providers, including Adobe, just by using an email address.

In this post, we’ll walk through how to retrieve Adobe profile details using AvatarAPI, including how to interpret the JSON response and especially the RawData payload.


πŸ“¬ Making the Request

To fetch a user’s Adobe profile via AvatarAPI, you’ll need to send a POST request to:

https://avatarapi.com/v2/api.aspx

Here’s a sample request payload:

{
"username" : "demo",
"password" : "demo___",
"provider" : "Adobe",
"email" : "falecom@sergiolimabroker.com.br"
}

Replace username and password with your actual AvatarAPI credentials.


πŸ“¦ Sample Response

Here’s what a successful response might look like:

{
"Name": "",
"Image": "https://mir-s3-cdn-cf.behance.net/user/276/63d4f1907178277.60f249006a140.jpg",
"Valid": true,
"City": "",
"Country": "",
"IsDefault": false,
"Success": true,
"RawData": "[{\"type\":\"individual\",\"authenticationMethods\":[{\"id\":\"google\",\"url\":\"https://adobeid-na1.services.adobe.com/renga-idprovider/social/v2/signin/google\"},{\"id\":\"password\",\"url\":null}],\"status\":{\"code\":\"active\",\"reason\":null},\"images\":{\"50\":\"https://mir-s3-cdn-cf.behance.net/user/50/63d4f1907178277.60f249006a140.jpg\",\"100\":\"https://mir-s3-cdn-cf.behance.net/user/100/63d4f1907178277.60f249006a140.jpg\",\"115\":\"https://mir-s3-cdn-cf.behance.net/user/115/63d4f1907178277.60f249006a140.jpg\",\"138\":\"https://mir-s3-cdn-cf.behance.net/user/138/63d4f1907178277.60f249006a140.jpg\",\"230\":\"https://mir-s3-cdn-cf.behance.net/user/230/63d4f1907178277.60f249006a140.jpg\",\"276\":\"https://mir-s3-cdn-cf.behance.net/user/276/63d4f1907178277.60f249006a140.jpg\"},\"hasT2ELinked\":false}]",
"Source": {
"Name": "Adobe"
}
}

🧠 Understanding the Response Fields

  • Image: URL of the profile image, typically hosted on Behance CDN.
  • Valid: Boolean indicating whether a profile was found.
  • IsDefault: Tells you whether the image is a generic placeholder or a custom user-uploaded image.
  • Success: Boolean status of the request.
  • Source: Confirms that the provider is Adobe.
  • RawData: This is a stringified JSON array with more granular details from the provider.

πŸ” Deep Dive into RawData

Let’s parse the RawData field for a better understanding:

[
{
"type": "individual",
"authenticationMethods": [
{
"id": "google",
"url": "https://adobeid-na1.services.adobe.com/renga-idprovider/social/v2/signin/google"
},
{
"id": "password",
"url": null
}
],
"status": {
"code": "active",
"reason": null
},
"images": {
"50": "https://...50.jpg",
"100": "https://...100.jpg",
"115": "https://...115.jpg",
"138": "https://...138.jpg",
"230": "https://...230.jpg",
"276": "https://...276.jpg"
},
"hasT2ELinked": false
}
]

Key Elements:

  • type: Usually "individual", indicating a personal profile.
  • authenticationMethods: Shows available login methods for this Adobe account (e.g., Google, password).
  • status: Account status, with code typically being "active".
  • images: Dictionary of image sizes for the profile picture. Useful for responsive UI.
  • hasT2ELinked: Indicates whether the account is linked to two-factor authentication (T2E = Two-step or Two-element?).

🎯 Practical Use Case

If you’re building a user-facing dashboard, you can display a verified profile image directly using the Image field, and optionally allow your backend to parse RawData for more advanced logic, such as:

  • Detecting authentication methods.
  • Validating account status.
  • Choosing image resolution dynamically based on device.

Example (JavaScript):

const profileImgUrl = response.Image;
document.getElementById('avatar').src = profileImgUrl;

πŸ§ͺ Pro Tip: Parsing RawData

Since RawData is a stringified JSON array, you’ll need to parse it manually:

const rawData = JSON.parse(response.RawData);
console.log(rawData[0].authenticationMethods);

βœ… Conclusion

Using AvatarAPI, retrieving Adobe profile details is seamless and efficient. The rich JSON response, especially the RawData, provides developers with a powerful toolkit to enhance user profiles, drive personalization, or even verify identity with minimal effort.


If you’re using AvatarAPI in production or need help parsing the Adobe-specific data, feel free to reach out or drop your questions in the comments!

Categories: Uncategorized

How to Use the Car Rego API in WordPress

In this tutorial, we’ll walk through how to integrate the Car Registration API into a WordPress site. This method ensures that your username and API key are not publicly exposed.

Tools Used in This Demo

  1. WordPress
  2. Code Snippets (Free plugin)
  3. Elementor Pro (Paid website builder)

We’ll be using both PHP and JavaScript scripts. While there are multiple ways to accomplish this, the approach described here prioritizes security by keeping sensitive credentials hidden.


Step 1: Register for the API

Visit carregistrationapi.com and create an account to get your username/API key.


Step 2: Add the PHP Script

Use the Code Snippets plugin to add the PHP code. Paste the script into a new snippet and activate it.

πŸ” Important: Replace the placeholder on line 16 with your actual username from Step 1.

add_action('elementor_pro/forms/new_record', function($record, $handler) {
    if (!session_id()) session_start();

    // βœ… Confirm hook fired
    //file_put_contents(WP_CONTENT_DIR . '/uploads/rego-debug.txt', "HOOK FIRED\n", FILE_APPEND);
    //check https://abc.com.au/wp-content/uploads/rego-debug.txt

    $form_id = $record->get_form_settings('form_id');
    if ('regoform' !== $form_id) return;

    //file_put_contents(WP_CONTENT_DIR . '/uploads/rego-debug.txt', "Form matched: $form_id\n", FILE_APPEND);

    $fields = $record->get('fields');
    $rego  = $fields['rego']['value'] ?? '';
    $state = $fields['state']['value'] ?? '';
    $username = 'ABC';

    $api_url = add_query_arg([
        'RegistrationNumber' => $rego,
        'State'              => $state,
        'username'           => $username,
    ], 'https://www.regcheck.org.uk/api/reg.asmx/CheckAustralia');

    // πŸ” API call
    $response = wp_remote_get($api_url);

    // βœ… ADD LOGGING BLOCK HERE
    //file_put_contents(WP_CONTENT_DIR . '/uploads/rego-debug.txt', "API URL: $api_url\n", FILE_APPEND);

    if (is_wp_error($response)) {
        $error_message = $response->get_error_message();
        file_put_contents(WP_CONTENT_DIR . '/uploads/rego-debug.txt', "API Error: $error_message\n", FILE_APPEND);
    } else {
        $body = wp_remote_retrieve_body($response);
        //file_put_contents(WP_CONTENT_DIR . '/uploads/rego-debug.txt', "API Response: $body\n", FILE_APPEND);

        // Parse response
        $xml = @simplexml_load_string($body);
        $vehicleJson = (string) ($xml->vehicleJson ?? '');
        $vehicleData = json_decode($vehicleJson, true);

        $_SESSION['rego_result'] = [
            'Description' => $vehicleData['Description'] ?? 'N/A',
            'Year'        => $vehicleData['RegistrationYear'] ?? 'N/A',
            'Engine'      => $vehicleData['Engine'] ?? 'N/A',
        ];
    }
}, 10, 2);

// Shortcode to display result
add_shortcode('rego_vehicle_info', function() {
    if (!session_id()) session_start();
	
	//$log_path = WP_CONTENT_DIR . '/uploads/rego-debug.txt';

    // Log shortcode execution
    //file_put_contents($log_path, "SHORTCODE TRIGGERED\n", FILE_APPEND);
	
    $data = $_SESSION['rego_result'] ?? null;

    if (!$data) {
        return '<p>No vehicle information available yet.</p>';
    }
	
    //file_put_contents($log_path, "Session Data: " . print_r($data, true) . "\n", FILE_APPEND);
	
    ob_start();
    ?>
    <div class="rego-vehicle-info">
        <h3>Vehicle Details</h3>
        <ul>
            <li><strong>Description:</strong> <?= esc_html($data['Description']) ?></li>
            <li><strong>Registration Year:</strong> <?= esc_html($data['Year']) ?></li>
            <li><strong>Engine:</strong> <?= esc_html($data['Engine']) ?></li>
        </ul>
    </div>
    <?php
    return ob_get_clean();
});

add_action('rest_api_init', function() {
    register_rest_route('rego/v1', '/data', [
        'methods' => 'GET',
        'callback' => function() {
            if (!session_id()) session_start();
            return $_SESSION['rego_result'] ?? ['error' => 'No data in session'];
        },
        'permission_callback' => '__return_true'
    ]);
});

Step 3: Create the Form and Layout on Your Website

a) Create the Form (Using Elementor or similar)

Structure your form with the following settings:

  • Form ID: regoform
  • Form Name: regoform
  • Rego Field:
    • ID: rego
    • Shortcode: [field id="rego"]
  • State Field:
    • ID: state
    • Shortcode: [field id="state"]

These field IDs will be referenced in the PHP and JavaScript scripts.

b) Add HTML Widgets

Use two separate HTML widgets in Elementor:

  1. For Displaying the Result:
    Add this HTML element: <div id="rego-result-container"></div>
  2. For the JavaScript Logic:
    Add the JavaScript snippet (you can remove or comment out any log messages if not needed).
<script>
window.addEventListener('load', function () {
    function waitForjQueryAndForm() {
        if (typeof jQuery === 'undefined') {
            console.log("⏳ Waiting for jQuery...");
            setTimeout(waitForjQueryAndForm, 100);
            return;
        }

        jQuery(function ($) {
            const $form = $('#regoform');

            if (!$form.length) {
                console.log("⏳ Waiting for form #regoform to appear...");
                setTimeout(waitForjQueryAndForm, 100);
                return;
            }

            console.log("βœ… Found form #regoform");

            // Listen for form success response from Elementor Pro
            $form.on('submit_success', function () {
                console.log("βœ… Form #regoform successfully submitted via AJAX");

                setTimeout(function () {
                    $.ajax({
                        url: '/wp-json/rego/v1/data',
                        method: 'GET',
                        success: function (data) {
                            console.log("βœ… API response:", data);

                            if (data && !data.error) {
                                const html = `
                                    <div class="rego-vehicle-info">
                                        <h3>Vehicle Details</h3>
                                        <ul>
                                            <li><strong>Description:</strong> ${data.Description}</li>
                                            <li><strong>Registration Year:</strong> ${data.Year}</li>
                                            <li><strong>Engine:</strong> ${data.Engine}</li>
                                        </ul>
                                    </div>
                                `;
                                $('#rego-result-container').html(html);
                            } else {
                                $('#rego-result-container').html('<p>No vehicle info available.</p>');
                            }
                        },
                        error: function () {
                            $('#rego-result-container').html('<p>Failed to load vehicle info.</p>');
                        }
                    });
                }, 1000);
            });
        });
    }

    waitForjQueryAndForm();
});
</script>

How the Flow Works

  1. The user fills out the form and clicks the “Send” button.
  2. The form captures the rego and state values.
  3. The PHP script listens for the form action, calls the API, and fetches the results.
  4. The JavaScript reads the result and displays the data dynamically on the page.

Example

Here’s a basic demo in action

How to Query #LinkedIn from an #Email Address Using AvatarAPI.com

Introduction

When working with professional networking data, LinkedIn is often the go-to platform for retrieving user information based on an email address. Using AvatarAPI.com, developers can easily query LinkedIn and other data providers through a simple API request. In this guide, we’ll explore how to use the API to retrieve LinkedIn profile details from an email address.

API Endpoint

To query LinkedIn using AvatarAPI.com, send a request to:

https://avatarapi.com/v2/api.aspx

JSON Payload

A sample JSON request to query LinkedIn using an email address looks like this:

{
    "username": "demo",
    "password": "demo___",
    "provider": "LinkedIn",
    "email": "jason.smith@gmail.com"
}

Explanation of Parameters:

  • username: Your AvatarAPI.com username.
  • password: Your AvatarAPI.com password.
  • provider: The data source to query. In this case, “LinkedIn” is specified. If omitted, the API will search a default set of providers.
  • email: The email address for which LinkedIn profile data is being requested.

API Response

A successful response from the API may look like this:

{
    "Name": "Jason Smith",
    "Image": "https://media.licdn.com/dms/image/D4E12AQEud3Ll5MI7cQ/article-inline_image-shrink_1500_2232/0/1660833954461?e=1716422400&v=beta&t=r-9LmmNBpvS4bUiL6k-egJ8wUIpEeEMl9NJuAt7pTsc",
    "Valid": true,
    "City": "Los Angeles, California, United States",
    "Country": "US",
    "IsDefault": false,
    "Success": true,
    "RawData": "{\"resultTemplate\":\"ExactMatch\",\"bound\":false,\"persons\":[{\"id\":\"urn:li:person:DgEdy8DNfhxlX15HDuxWp7k6hYP5jIlL8fqtFRN7YR4\",\"displayName\":\"Jason Smith\",\"headline\":\"Creative Co-founder at Mega Ventures\",\"summary\":\"Jason Smith Head of Design at Mega Ventures.\",\"companyName\":\"Mega Ventures\",\"location\":\"Los Angeles, California, United States\",\"linkedInUrl\":\"https://linkedin.com/in/jason-smith\",\"connectionCount\":395,\"skills\":[\"Figma (Software)\",\"Facebook\",\"Customer Service\",\"Event Planning\",\"Social Media\",\"Sales\",\"Healthcare\",\"Management\",\"Web Design\",\"JavaScript\",\"Software Development\",\"Project Management\",\"APIs\"]}]}",
    "Source": {
        "Name": "LinkedIn"
    }
}

Explanation of Response Fields:

  • Name: The full name of the LinkedIn user.
  • Image: The profile image URL.
  • Valid: Indicates whether the returned data is valid.
  • City: The city where the user is located.
  • Country: The country of residence.
  • IsDefault: Indicates whether the data is a fallback/default.
  • Success: Confirms if the request was successful.
  • RawData: Contains additional structured data about the LinkedIn profile, including:
    • LinkedIn ID: A unique identifier for the user’s LinkedIn profile.
    • Display Name: The name displayed on the user’s profile.
    • Headline: The professional headline, typically the current job title or a short description of expertise.
    • Summary: A brief bio or description of the user’s professional background.
    • Company Name: The company where the user currently works.
    • Location: The geographical location of the user.
    • LinkedIn Profile URL: A direct link to the user’s LinkedIn profile.
    • Connection Count: The number of LinkedIn connections the user has.
    • Skills: A list of skills associated with the user’s profile, such as programming languages, software expertise, or industry-specific abilities.
    • Education History: Details about the user’s academic background, including universities attended, degrees earned, and fields of study.
    • Employment History: Information about past and present positions, including company names, job titles, and employment dates.
    • Projects and Accomplishments: Notable work the user has contributed to, certifications, publications, and other professional achievements.
    • Endorsements: Skill endorsements from other LinkedIn users, showcasing credibility in specific domains.
  • Source.Name: The data provider (LinkedIn in this case).

LinkedIn Rate Limiting

By default, LinkedIn queries are subject to rate limits. To bypass these limits, additional parameters can be included in the JSON request:

{
    "overrideAccount": "your_override_username",
    "overridePassword": "your_override_password"
}

Using these credentials allows queries to be processed without rate limiting. However, to enable this feature, you should contact AvatarAPI.com to discuss setup and access.

Conclusion

AvatarAPI.com provides a powerful way to retrieve LinkedIn profile data using just an email address. While LinkedIn is one of the available providers, the API also supports other data sources if the provider field is omitted. With proper setup, including rate-limit bypassing credentials, you can ensure seamless access to professional networking data.

For more details, visit AvatarAPI.com.

Get #GAIA ID from #Gmail using #AvatarAPI

In this blog post, we will explore how to retrieve a user’s name, profile picture, and GAIA ID from an email address using the AvatarAPI.

Introduction to AvatarAPI

AvatarAPI is a powerful tool that allows developers to fetch user information from various providers. In this example, we will focus on retrieving data from Google, but it’s important to note that AvatarAPI supports multiple providers.

Making a Request to AvatarAPI

To get started, you need to make a POST request to the AvatarAPI endpoint with the necessary parameters. Here’s a step-by-step guide:

Step 1: Endpoint and Parameters

  • Endpoint:Β https://avatarapi.com/v2/api.aspx
  • Parameters:
    • username: Your AvatarAPI username (e.g.,Β demo)
    • password: Your AvatarAPI password (e.g.,Β demo___)
    • provider: The provider from which to fetch data (e.g.,Β Google)
    • email: The email address of the user (e.g.,Β jenny.jones@gmail.com)

Step 2: Example Request

Here’s an example of how you can structure your request:

Copy{
    "username": "demo",
    "password": "demo___",
    "provider": "Google",
    "email": "jenny.jones@gmail.com"
}

Step 3: Sending the Request

You can use tools like Postman or write a simple script in your preferred programming language to send the POST request. Below is an example using Python with the requests library:

Copyimport requests

url = "https://avatarapi.com/v2/api.aspx"
data = {
    "username": "demo",
    "password": "demo___",
    "provider": "Google",
    "email": "jenny.jones@gmail.com"
}

response = requests.post(url, json=data)
print(response.json())

Step 4: Handling the Response

If the request is successful, you will receive a JSON response containing the user’s information. Here’s an example response:

Copy{
    "Name": "Jenny Jones",
    "Image": "https://lh3.googleusercontent.com/a-/ALV-UjVPreEBCPw4TstEZLnavq22uceFSCS3-KjAdHgnmyUfSA9hMKk",
    "Valid": true,
    "City": "",
    "Country": "",
    "IsDefault": true,
    "Success": true,
    "RawData": "108545052157874609391",
    "Source": {
        "Name": "Google"
    }
}

Understanding the Response

  • Name:Β The full name of the user.
  • Image:Β The URL of the user’s profile picture.
  • Valid:Β Indicates whether the email address is valid.
  • City and Country:Β Location information (if available).
  • IsDefault:Β Indicates if the returned data is the default for the provider.
  • Success:Β Indicates whether the request was successful.
  • RawData:Β The GAIA ID, which is a unique identifier for the user.
  • Source:Β The provider from which the data was fetched.

Other Providers

While this example focuses on Google, AvatarAPI supports other providers as well. You can explore the AvatarAPI documentation to learn more about the available providers and their specific requirements.

Conclusion

Using AvatarAPI to retrieve user information from an email address is a straightforward process. By sending a POST request with the necessary parameters, you can easily access valuable user data such as name, profile picture, and GAIA ID. This information can be instrumental in enhancing user experiences and integrating with various applications.

Stay tuned for more insights on leveraging APIs for efficient data retrieval!

Categories: Uncategorized Tags: , , , ,

Database of Malaysian #NVIC codes available on #PayHip

https://payhip.com/b/STZvc

Malaysian NVIC Vehicle Database – 23,715 Vehicle Models

Comprehensive NVIC Database for Malaysian Vehicles

Our Malaysian NVIC Vehicle Database provides a detailed reference for 23,715 car makes and models, covering essential vehicle details along with insurance and valuation data. This dataset is invaluable for automotive businesses, insurance companies, financial institutions, and regulatory bodies that require accurate and up-to-date vehicle information.

Key Features:

βœ… Extensive Coverage – Includes 23,715 unique vehicle records spanning multiple manufacturers and models.
βœ… Structured Data – Organized into 18 columns for easy reference and integration.
βœ… Complete Vehicle Identification – Features NVIC codes, model year, make, family, variant, series, and body style.
βœ… Engine & Transmission Details – Provides engine capacity (CC) and transmission type.
βœ… Market Valuation Data – Includes new and used prices (WM & EM), recommended retail price (RRR), and sum insured values.
βœ… Historical Updates – Contains timestamps for data creation, updates, and valuation dates.

Column Breakdown:

  • Id – Unique identifier for each vehicle.
  • NVIC – National Vehicle Identification Code.
  • Year – Model year of the vehicle.
  • Make – Manufacturer (e.g., Toyota, Honda, Proton).
  • Family – General vehicle classification within the brand.
  • Variant – Specific model variation.
  • Series – Trim or sub-model series.
  • Style – Body style (e.g., sedan, SUV, hatchback).
  • CC – Engine displacement (in cubic centimeters).
  • Transmission – Type of gearbox (manual/automatic).
  • WM New Price & RRR – West Malaysia pricing data.
  • EM New Price & RRR – East Malaysia pricing data.
  • Sum Insured – Suggested insured value.
  • TimeCreated & TimeUpdated – Timestamps for record creation and last modification.
  • ValuationDate – Date of the latest vehicle valuation.

Use Cases:

πŸ”Ή Insurance & Valuation – Provides essential data for insurance pricing and underwriting.
πŸ”Ή Automotive Sales & Financing – Assists in loan approvals and resale evaluations.
πŸ”Ή Fleet Management & Logistics – Helps businesses manage and assess vehicle assets.
πŸ”Ή Regulatory Compliance & Government Agencies – Useful for vehicle registration and taxation.

This dataset is available in a structured format (CSV, SQL, or API-ready) for seamless integration into your systems.

More info here: https://payhip.com/b/STZvc

Source: https://www.vehicleapi.com.my/

Categories: Uncategorized

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

  1. Handles API Requests Securely: The method sets various security protocols (Ssl3, Tls, Tls11, Tls12) to ensure compatibility with Skype’s API.
  2. Custom Headers for Authentication: It constructs an HTTP request with necessary headers, including x-skypetoken, x-skype-client, and others.
  3. Manages Rate Limits & Token Refresh: If the API responds with an empty result (potentially due to a 429 Too Many Requests error), the token is refreshed, and the search is retried up to five times.
  4. Enhances API Response: The method modifies the API response to include an additional avatarImageUrl field 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;
        }
    }
}
Categories: Uncategorized Tags: , , , ,

Resolving Unauthorized Error When Deploying an #Azure Function via #ZipDeploy

Deploying an Azure Function to an App Service can sometimes result in an authentication error, preventing successful publishing. One common error developers encounter is:

Error: The attempt to publish the ZIP file through https://<function-name>.scm.azurewebsites.net/api/zipdeploy failed with HTTP status code Unauthorized.

This error typically occurs when the deployment process lacks the necessary authentication permissions to publish to Azure. Below, we outline the steps to resolve this issue by enabling SCM Basic Auth Publishing in the Azure Portal.

Understanding the Issue

The error indicates that Azure is rejecting the deployment request due to authentication failure. This often happens when the SCM (Kudu) deployment service does not have the correct permissions enabled, preventing the publishing process from proceeding.

Solution: Enable SCM Basic Auth Publishing

To resolve this issue, follow these steps:

  1. Open the Azure Portal and navigate to your Function App.
  2. In the left-hand menu, select Configuration.
  3. Under the General settings tab, locate SCM Basic Auth Publishing.
  4. Toggle the setting to On.
  5. Click Save and restart the Function App if necessary.

Once this setting is enabled, retry the deployment from Visual Studio or your chosen deployment method. The unauthorized error should now be resolved.

Additional Considerations

  • Use Deployment Credentials: If you prefer not to enable SCM Basic Auth, consider setting up deployment credentials under Deployment Center β†’ FTP/Credentials.
  • Check Azure Authentication in Visual Studio: Ensure that you are logged into the correct Azure account in Visual Studio under Tools β†’ Options β†’ Azure Service Authentication.
  • Use Azure CLI for Deployment: If problems persist, try deploying with the Azure CLI:az functionapp deployment source config-zip \ --resource-group <resource-group> \ --name <function-app-name> \ --src <zip-file-path>

By enabling SCM Basic Auth Publishing, you ensure that Azure’s deployment service can authenticate and process your function’s updates smoothly. This quick fix saves time and prevents unnecessary troubleshooting steps.

Categories: Uncategorized Tags: , , , ,