ASP.NET C# Consume External API & Save JSON Data To MS SQL Table – Part I

Overview

With a wide variety of free APIs, many of them still have limits on how many times you can call them a day. Also, some may not offer access to historical data. In this example, we will call a free API for the Stock Price Data and store the results in a table.

Bonus. Part II. We will create and call a stored procedure with Entity Frameworks, to backup the data to an historical table and then delete/replace the data in the main table so it can be run on scheduler.

MS SQL Table

Create a table called StockPriceHistory .

CREATE TABLE [dbo].[PriceHistory](
	[Id] [int] NOT NULL,
	[TickerSymbol] [nvarchar](50) NULL,
	[ClosingPrice] [float] NULL,
	[High] [float] NULL,
	[Low] [float] NULL,
	[ClosingDate] [datetime2](7) NULL
);

ALTER TABLE [dbo].[PriceHistory]
   ADD Id INT IDENTITY

ALTER TABLE [dbo].[PriceHistory]
   ADD CONSTRAINT PK_PriceHistory
   PRIMARY KEY(Id);

Controller

The controller action below will get yesterday and todays date to get the last day of prices.

Libraries used for the controller

using System;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Linq;
using System.Net;
using System.Web.Http;
using System.Web.Http.Description;
using VirtualInvestorAPI.Models;
using System.Web.Http.Cors;
using Newtonsoft.Json.Linq;
using System.IO;

JSON DATA Format

JSON Data received.

{
  "symbol" : "AAPL",
  "historical" : [ {
    "date" : "2020-04-03",
    "open" : 242.8,
    "high" : 245.7,
    "low" : 238.97,
    "close" : 241.41,
    "adjClose" : 241.41,
    "volume" : 3.24182E7,
    "unadjustedVolume" : 3.24182E7,
    "change" : 1.39,
    "changePercent" : 0.572,
    "vwap" : 242.02667,
    "label" : "April 03, 20",
    "changeOverTime" : 0.00572
  } ]
}

CORS and Route Prefix

The controller uses attribute routing (Optional). The prefix is as follows:

    [EnableCors(origins: "*", headers: "*", methods: "*")]
    [RoutePrefix("api/pricehistory")]
    public class PriceHistoryController : ApiController
    {

Format String to Float

Add a function to format the float to 2 decimal places.

  private static float formatFloat(string val)
        {            
            float floatVal = float.Parse(val);
            string floatFMT = floatVal.ToString("n2");
            return float.Parse(floatFMT);
        }

All together

The Controller Action code allows for one parameter for the company or ticker symbol to be passed. The request will get a single day of data.

[HttpGet]
[Route("pricehistoryjob/{company}")]
public object PriceHistoryJob(string company)
{
    try
    {
        var today = DateTime.Today.Date.ToString("MM/dd/yyyy");                
        string apiUrl = "https://financialmodelingprep.com/api/v3/historical-price-full/" + company + " ?from=" + today + " &to=" + today;

        var request = (HttpWebRequest)WebRequest.Create(apiUrl);

        request.Method = "GET";
        request.AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip;

        var content = string.Empty;

        using (var response = (HttpWebResponse)request.GetResponse())
        {
            using (var stream = response.GetResponseStream())
            {
                using (var sr = new StreamReader(stream))
                {
                    content = sr.ReadToEnd();
                }
            }
        }

        var dataArr = JObject.Parse(content);
                
        string TickerVal = (string)dataArr["symbol"];
        var TickerSymbolValue = TickerVal.ToString().TrimStart('[').TrimEnd(']').TrimStart('"').TrimEnd('"');
        tableRecord.TickerSymbol = TickerSymbolValue.Replace('"', ' ').Trim(); ;

        var closeVal = dataArr["historical"][0]["high"];
                
        tableRecord.ClosingPrice = formatFloat(closeVal.ToString());

        var highVal = dataArr["historical"][0]["high"];
        tableRecord.High = formatFloat(highVal.ToString());

        var lowVal = dataArr["historical"][0]["low"];
        tableRecord.Low = formatFloat(lowVal.ToString());

        var dateVal = dataArr["historical"][0]["date"];
        tableRecord.ClosingDate = DateTime.Parse(dateVal.ToString());

        db.PriceHistories.Add(tableRecord);
        db.SaveChanges();
                
        return new { status = 200, msg = "Success"};
    }     
    catch (Exception e)
    {
        return new { status = 401, msg = e.InnerException};
    }
}