R Markdown

An example for calling Linkedin API from R, using 3-legged OAUTH 2.0 tokens. This R program, uses HTTR to make REST API calls to Linkedin. see https://linkedin.com/developers for more information on Linkedin API V2

Linkedin API V2 - restricts user access based on privileges granted. To get legal access to the V2 end points - please create an APP - as mentioned below: 1. Login to Linkedin 2. Go to Linkedin.com/developers 3. Click on Create APP 4. A client ID, Secret - automatically gets generated by Linkedin 5. provide the OAUTH 2.0 redirect url 6. Select “Sign in with Linkedin API V2” product - this gives access to /me, /profile

#set httr_oauth_cache to TRUE
#set httr_oob_default to TRUE
options(httr_oob_default = "TRUE")
options(httr_oauth_cache = "TRUE")

defining end points

HTTR library contains many commonly used endpoints such as Git, Google, Linkedin etc. The linekdin endpoint - is for OAUTH 1.0 and its pointed to old auth urls, which is shown below:

linkedin_auth1 <- oauth_endpoint("requestToken", "authorize", "accessToken",
                           base_url = "https://api.linkedin.com/uas/oauth"
)

define the linkedin - OAUTH endpoint as below, to use the latest auth and access urls:

linkedin <- oauth_endpoint(
  authorize = "https://www.linkedin.com/oauth/v2/authorization",
  access = "https://reinventanalytics.com/"
)

set the client ID, Secret values as variables, for later use in initializing OAUTH tokens Scope - is a privilege granted to you. Linkedin grants 2 privilages/scopes “r_liteprofile”, “r_basicprofile” in this example, I am using r_liteprofile” as scope. Redirect URL - is your website url, where you want to receive the Authorization code. Authorization Code is required to create OAUTH 2.0 token

r_client_id = "789fdykj5bh7lr"
r_redirect_uri = "https://reinventanalytics.com/"
r_scope = "r_liteprofile"

#use this r_nonce as state parameter value, to track the responses with the same value
r_nonce <- as.numeric(as.POSIXct(Sys.time()))

call oauth_app function with app name, key - client ID, secret - client secret, redirect url; all these values should match with values in your Linkedin APP page.

oauth_app(appname="myjobsearch", key=r_client_id, secret = r_client_secret, redirect_uri = r_redirect_uri) -> linkedin_app

Create OAUTH 2.0 token

oauth2.0_token(
  endpoint= linkedin,
  app=linkedin_app,
  scope = r_scope,
  user_params = NULL,
  type = NULL,
  use_oob = TRUE,
  oob_value = linkedin_app$redirect_uri,
  as_header = TRUE,
  use_basic_auth = FALSE,
  cache = getOption("httr_oauth_cache"),
  config_init = list(),
  client_credentials = FALSE,
  credentials = NULL,
  query_authorize_extra = list()
) -> at2
at2
## <Token>
## <oauth_endpoint>
##  authorize: https://www.linkedin.com/oauth/v2/authorization
##  access:    https://reinventanalytics.com/
## <oauth_app> myjobsearch
##   key:    789fdykj5bh7lr
##   secret: <hidden>
## <credentials> node, doc
## ---

this function call, opens a new tab in the default browser, the redirect url we gave in our token will be appended with the Authorization Code.

Since this is a 3-legged OAUTH, we need to copy the Authorization Code: from the redirect url in the browser tab.

Configure the token - for later use in the HTTR::GET calls.

#pass at2 as token in following url requests
linkedin_token <- config(token = at2)
linkedin_token
## <request>
## Auth token: Token2.0

Making a GET call to /jobs. with token (at2) configured.

Print response object of the GET call.

To check whether the configured token is used in the GET calls. the actual url used (after any redirects), the http status, the file (content) type, the size, and if it’s a text file, the first few lines of output.

httr::GET(url = "https://www.linkedin.com/jobs/",user_agent(agent = "Rstudio"),config(token = at2),
          content_type(type = "text/html"), verbose()) -> r_jobs_main

#GET call response details are as follows:
r_jobs_main
## Response [https://www.linkedin.com/jobs/]
##   Date: 2023-04-29 03:08
##   Status: 200
##   Content-Type: text/html; charset=utf-8
##   Size: 128 kB
## <!DOCTYPE html>
## 
## 
##     
##     
##     
##     
##     
##     
##     
## ...
content(r_jobs_main)-> r_jobs_main_resp

Authorization: Bearer

user agent : RStudio

indicates that our oauth token is being used by the GET calls.

lets pull out important parts of the response with various helper methods, or dig directly into the object:

status_code (r_jobs_main)
## [1] 200
headers(r_jobs_main)
## $`cache-control`
## [1] "no-cache, no-store"
## 
## $pragma
## [1] "no-cache"
## 
## $`content-length`
## [1] "15227"
## 
## $`content-type`
## [1] "text/html; charset=utf-8"
## 
## $`content-encoding`
## [1] "gzip"
## 
## $expires
## [1] "Thu, 01 Jan 1970 00:00:00 GMT"
## 
## $vary
## [1] "Accept-Encoding"
## 
## $`set-cookie`
## [1] "JSESSIONID=ajax:6145912090416498618; SameSite=None; Path=/; Domain=.www.linkedin.com; Secure"
## 
## $`set-cookie`
## [1] "lang=v=2&lang=en-us; SameSite=None; Path=/; Domain=linkedin.com; Secure"
## 
## $`set-cookie`
## [1] "bcookie=\"v=2&f8b531ac-3872-4649-81fc-f45113d1955d\"; domain=.linkedin.com; Path=/; Secure; Expires=Sun, 28-Apr-2024 03:08:31 GMT; SameSite=None"
## 
## $`set-cookie`
## [1] "bscookie=\"v=1&20230429030831f45d34c9-72b9-497e-8fb0-855a0ab469abAQGvZRR4onqXdyt1FWTKR7eCRdvvDwLB\"; domain=.www.linkedin.com; Path=/; Secure; Expires=Sun, 28-Apr-2024 03:08:31 GMT; HttpOnly; SameSite=None"
## 
## $`set-cookie`
## [1] "lidc=\"b=OGST07:s=O:r=O:a=O:p=O:g=2551:u=1:x=1:i=1682737711:t=1682824111:v=2:sig=AQFQ4fP6H5v-eqDn-_1IXc5o_J5DdC9o\"; Expires=Sun, 30 Apr 2023 03:08:31 GMT; domain=.linkedin.com; Path=/; SameSite=None; Secure"
## 
## $`x-fs-uuid`
## [1] "0005fa70e7864cf45c0dc041f49ba0ec"
## 
## $`content-security-policy`
## [1] "default-src 'none'; connect-src 'self' *.licdn.com *.linkedin.com cdn.linkedin.oribi.io dpm.demdex.net/id lnkd.demdex.net blob: accounts.google.com/gsi/ linkedin.sc.omtrdc.net/b/ss/ *.microsoft.com *.adnxs.com; script-src 'report-sample' 'sha256-SSoodjUD3LGm2FfFCVHGqEb8D4UM3OOigidT2UKDcYg=' 'sha256-cKTgdnmO6+hXd85a9wKg1effVfVzenUAtUCyOKY9bQE=' 'sha256-DwtT8+ZZKpxH9pqZNAmJ3GdbLAh5SsYaXR3omTXPCns=' 'sha256-sV9jZa797T0QWBzcU/CNd4tpBhTnh+TFdLnfjlitl28=' 'sha256-aa/Q8CRBDSqTQbCIyioPhZaz+G+dbPyu7BzsjInEmiU=' 'sha256-THuVhwbXPeTR0HszASqMOnIyxqEgvGyBwSPBKBF/iMc=' 'sha256-zTIusdVJJeXz9+iox2a+pdDglzbpRpFVRzEwvW4AONk=' 'sha256-iC8MPqNLw0FDnsBf4DlSkFLNTwhkI85aouiAEB819ic=' 'sha256-2EqrEvcPzl8c6/TSGVvaVMEe7lg700MAz/te4/3kTYY=' 'sha256-y5uW69VItKj51mcc7UD9qfptDVUqicZL+bItEpvVNDw=' 'sha256-DatsFGoJ8gFkzzxo47Ou76WZ+3QBPOQHtBu9p9b3DhA=' 'sha256-k95cyM8gFgPziZe5VQ2IvJvBUVyd5zFt2CokIUwqdHE=' 'sha256-PyCXNcEkzRWqbiNr087fizmiBBrq9O6GGD8eV3P09Ik=' 'sha256-2SQ55Erm3CPCb+k03EpNxU9bdV3XL9TnVTriDs7INZ4=' 'sha256-S/KSPe186K/1B0JEjbIXcCdpB97krdzX05S+dHnQjUs=' 'sha256-9pXOIwF4N0gPltLd3AI69lkCjSC2H/Eb3sc5zdmUyYU=' 'sha256-jou6v/Nleyzoc+LXktAv1Fp8M807dVVxy7E/yzVljHc=' 'sha256-6E4e/3dSvj/8JZT2S2yR91mspqM6MyOpKl5lrhHsZa8=' 'sha256-3woF8BZ54TeXM+czaH3aXoaJsVpiamuAKFsXDykAR/Q=' 'sha256-vIfNcKb8ixJg1cfJIoNNYjWcm0lezj1/XpUNFiZyVsU=' 'sha256-cLsHUHFgT/VGX04cZrJ9xgm4HbzTR7ptutkxK+7BlMk=' 'sha256-BwU8jMnQYUhjOpsDVABpfddV/DlP1ZYrFcTumYw7x54=' 'sha256-wz6ika9i3WU3bpUPdhYDZeO/NrDQniDyiscN0LWnyaY=' snap.licdn.com platform.linkedin.com platform-akam.linkedin.com platform-ecst.linkedin.com platform-azur.linkedin.com static.licdn.com static-exp1.licdn.com static-exp2.licdn.com static-exp3.licdn.com; img-src data: blob: * android-webview-video-poster:; font-src data: *; style-src 'self' 'unsafe-inline' static.licdn.com static-exp1.licdn.com static-exp2.licdn.com static-exp3.licdn.com; media-src *.licdn.com *.lynda.com; worker-src 'self' blob: static.licdn.com static-exp1.licdn.com static-exp2.licdn.com static-exp3.licdn.com; frame-src 'self' www.youtube.com/embed/ www.youtube-nocookie.com/embed/ lnkd.demdex.net smartlock.google.com accounts.google.com player.vimeo.com *.linkedin.com www.slideshare.net *.megaphone.fm *.omny.fm *.sounder.fm msit.powerbi.com app.powerbi.com linkedin.github.io *.licdn.com *.adnxs.com acdn.adnxs-simple.com radar.cedexis.com; frame-ancestors 'self' *.www.linkedin.com:*; manifest-src 'self'; report-uri https://www.linkedin.com/security/csp?f=gg"
## 
## $`x-frame-options`
## [1] "sameorigin"
## 
## $`x-content-type-options`
## [1] "nosniff"
## 
## $`strict-transport-security`
## [1] "max-age=31536000"
## 
## $`expect-ct`
## [1] "max-age=86400, report-uri=\"https://www.linkedin.com/platform-telemetry/ct\""
## 
## $`x-li-fabric`
## [1] "prod-lor1"
## 
## $`x-li-pop`
## [1] "afd-prod-lor1-x"
## 
## $`x-li-proto`
## [1] "http/2"
## 
## $`x-li-uuid`
## [1] "AAX6cOeGTPRcDcBB9Jug7A=="
## 
## $`x-cache`
## [1] "CONFIG_NOCACHE"
## 
## $`x-msedge-ref`
## [1] "Ref A: D71C2D00FA94476F98966F10348EC519 Ref B: PAOEDGE0609 Ref C: 2023-04-29T03:08:31Z"
## 
## $date
## [1] "Sat, 29 Apr 2023 03:08:31 GMT"
## 
## attr(,"class")
## [1] "insensitive" "list"
stop_for_status(r_jobs_main)