7

If I go to SharePoint 2013 site, click on Admin -> SharePoint, you will see a list of site collections (the link is https://somename-admin.sharepoint.com/_layouts/15/online/SiteCollections.aspx)

What is the REST endpoint URL to get this list of site collections ?

I was trying _api/search/query?querytext='contentclass:sts_site' or _api/search/query?querytext='contentclass:sts_web', but it doesn't give the whole list...

Waqas Sarwar MVP
  • 57,008
  • 17
  • 43
  • 79
Alex
  • 71
  • 1
  • 1
  • 2
  • However, as I said, "querytext='contentclass:sts_site" doesn't give the whole list of site collections. It returns only those accessible for current user. This is the problem. –  Sep 02 '14 at 13:07
  • how were you able to solve the problem? the answers to this question don't work for me. I only get a subset of Sites. Interestingly, i don't get some of the Site collections for which I am the owner! – Syed Mauze Rehan Aug 01 '15 at 10:54

5 Answers5

11

Unfortunately it doesn't seem possible to achieve using neither REST nor JSOM.

So, the workaround, as you correctly mentioned in the question would be to utilize SharePoint Search REST/JSOM and specify contentclass:sts_site to return sites results.

The following REST example demonstrates that:

function searchSites(webUrl,success, failure) {
    var url = webUrl + "/_api/search/query?querytext='contentclass:sts_site'";
    $.ajax({
        url: url,
        method: "GET",
        headers: { "Accept": "application/json; odata=verbose" },
        success: function (data) {
            success(data.d.query);
        },
        error: function (data) {
            failure(data);
        }
    });
}


//print sites info
searchSites(_spPageContextInfo.webAbsoluteUrl,
  function(query){
      var resultsCount = query.PrimaryQueryResult.RelevantResults.RowCount;
      for(var i = 0; i < resultsCount;i++) {
          var row = query.PrimaryQueryResult.RelevantResults.Table.Rows.results[i];
          var siteUrl = row.Cells.results[6].Value;
          console.log(JSON.stringify(siteUrl));
      }   
  },
  function(error){
    console.log(JSON.stringify(error));
  }
);

EDIT:

Note this is using the search query to get the list of site collections. as such, newly added site collections will not appear until the search index is recrawled.

Also this is very unfortunate that this requires being an administrator to do this. I created a new feature request Sharepoint user voice idea for not requiring admin rights to list site collections to handle this.

Nicholas DiPiazza
  • 717
  • 2
  • 16
  • 42
Vadim Gremyachev
  • 42,498
  • 3
  • 86
  • 167
  • This doesn't work for me. I am using Client Credentials Flow to get access token. Is that the issue? URL i hit

    https://{tenantName}-admin.sharepoint.com/_api/search/query?querytext='....

    – Syed Mauze Rehan Jul 24 '15 at 05:14
  • How will this work for Anonymous access? (APP only auth token) – Syed Mauze Rehan Jul 31 '15 at 07:06
  • @Vadim doesn't work for me either. This just returns a bunch of stuff values, but it does not have the 2 site collections i created in the admin UI. Is there literally no way to list site collections in the entire API? that's really unfortunate – Nicholas DiPiazza May 03 '18 at 05:29
  • 1
    To anyone not seeing newly created site collections from this query, it's because the search index hasn't added the site collections yet! you need to either wait or trigger the indexing before this will work! – Nicholas DiPiazza May 04 '18 at 13:54
  • Another problem is many sharepoint on-prem installations don't have the search service enabled: The search request was unable to connect to the Search Service. – Nicholas DiPiazza Aug 01 '18 at 17:40
  • OK so this does work on SharePoint online. But you need to filter using WebTemplate startsWith STS. And you need to use paging in case you have multiple pages. Use StartRow, and RowLimit as the paging parameters. Keep making requests until NumRows < RowLimit – Nicholas DiPiazza Apr 11 '21 at 01:35
2

There are no REST endpoints at the farm level in SharePoint, so there is no REST endpoint for all the site collections in your farm/tenancy.

You could get all the subwebs for a specific site (/_api/web?$select=Webs&$expand=Webs), but not all the sites in a tenant/farm.

John-M
  • 5,930
  • 3
  • 18
  • 36
2

For SharePoint on-prem

If the search API is enabled for you, use this answer above: https://sharepoint.stackexchange.com/a/113625/60157

Otherwise...

Soap call - sites.asmx - GetContent

One sure-fire way to get the site collections on your system with REST calls is to make call Soap with your REST client.

You have to make 2+ soap calls to get the result.

C# Code Example:

    public XmlDocument getContent(string siteUrl, String contentType, string contentId) {
      List<string> allSites = new List<string>();
      var _url = string.Format("{0}/_vti_bin/SiteData.asmx", siteUrl);
      var _action = "http://schemas.microsoft.com/sharepoint/soap/GetContent";

      XmlDocument soapEnvelopeXml = CreateSoapEnvelope(contentType, contentId);
      HttpWebRequest webRequest = CreateWebRequest(_url, _action);
      InsertSoapEnvelopeIntoWebRequest(soapEnvelopeXml, webRequest);

      // begin async call to web request.
      IAsyncResult asyncResult = webRequest.BeginGetResponse(null, null);

      // suspend this thread until call is complete. You might want to
      // do something usefull here like update your UI.
      asyncResult.AsyncWaitHandle.WaitOne();

      // get the response from the completed web request.
      XmlDocument contentDatabaseResult = new XmlDocument();
      using (WebResponse webResponse = webRequest.EndGetResponse(asyncResult)) {
        using (StreamReader rd = new StreamReader(webResponse.GetResponseStream())) {
          contentDatabaseResult.Load(rd);
        }
      }
      return contentDatabaseResult;
    }

    public List<string> GetAllSiteCollections() {
      List<String> allSites = new List<string>();
      XmlDocument virtualServerGetContentResult = getContent(rootSite, "VirtualServer", null);
      XmlNode contentResultNode = virtualServerGetContentResult.SelectSingleNode("//*[local-name() = 'GetContentResult']");
      if (contentResultNode == null || contentResultNode.InnerText == null) {
        throw new Exception(string.Format("Cannot list top level sites from {0}", rootSite));
      }
      XmlDocument innerXmlDoc = new XmlDocument();
      innerXmlDoc.LoadXml(contentResultNode.InnerText);
      string contentDatabaseId = innerXmlDoc.SelectSingleNode("//*[local-name() = 'ContentDatabase']").Attributes["ID"].Value;
      if (contentDatabaseId == null) {
        throw new Exception(string.Format("Cannot list top level sites from {0}", rootSite));
      }
      XmlDocument contentDatabaseGetContentResult = getContent(rootSite, "ContentDatabase", contentDatabaseId);
      XmlNode contentDatabaseResultNode = contentDatabaseGetContentResult.SelectSingleNode("//*[local-name() = 'GetContentResult']");
      if (contentDatabaseResultNode == null || contentDatabaseResultNode.InnerText == null) {
        throw new Exception(string.Format("Cannot list top level sites from {0}", rootSite));
      }
      innerXmlDoc = new XmlDocument();
      innerXmlDoc.LoadXml(contentDatabaseResultNode.InnerText);
      XmlNodeList sites = innerXmlDoc.SelectNodes("//*[local-name() = 'Site']");
      foreach (XmlNode siteNode in sites) {
        allSites.Add(siteNode.Attributes["URL"].Value);
      }
      return allSites;
    }

    HttpWebRequest CreateWebRequest(string url, string action) {
      HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(url);
      webRequest.Headers.Add("SOAPAction", action);
      webRequest.ContentType = "text/xml;charset=\"utf-8\"";
      webRequest.Accept = "text/xml";
      webRequest.Method = "POST";
      webRequest.Credentials = credentialCache;
      return webRequest;
    }

    XmlDocument CreateSoapEnvelope(string objectType, string objectId) {
      XmlDocument soapEnvelopeDocument = new XmlDocument();
      string soapEnv = @"<soapenv:Envelope xmlns:soapenv=""http://schemas.xmlsoap.org/soap/envelope/"" xmlns:soap=""http://schemas.microsoft.com/sharepoint/soap/"">
   <soapenv:Header/>
   <soapenv:Body>
      <soap:GetContent>
         <soap:objectType>{0}</soap:objectType>
{1}
         <soap:retrieveChildItems>true</soap:retrieveChildItems>
         <soap:securityOnly>false</soap:securityOnly>
      </soap:GetContent>
   </soapenv:Body>
</soapenv:Envelope>";
      soapEnvelopeDocument.LoadXml(string.Format(soapEnv, objectType,
                                                 objectId == null ? "" : "<soap:objectId>" + objectId + "</soap:objectId>"));
      return soapEnvelopeDocument;
    }

    void InsertSoapEnvelopeIntoWebRequest(XmlDocument soapEnvelopeXml, HttpWebRequest webRequest) {
      using (Stream stream = webRequest.GetRequestStream()) {
        soapEnvelopeXml.Save(stream);
      }
    }
  }

For SharePoint Online

Rest call - WCF ProcessQuery

First see https://pholpar.wordpress.com/2011/05/25/interpreting-network-traffic-generated-by-the-sharepoint-client-object-model/ for how to make rest calls to the WCF client.svc endpoint. I will not include the process of creating SPOIDCRL cookie and getting the X-Digest value. But you will need these.

curl -X POST \
  https://xxxxxxxxx-admin.sharepoint.com/_vti_bin/client.svc/ProcessQuery \
  -H 'Content-Type: text/xml' \
  -H 'Cookie: SPOIDCRL=77u/PD94b.....TwvU1A+' \
  -H 'X-ClientService-ClientTag: TAPS (16.0.7813.0)' \
  -H 'X-FORMS_BASED_AUTH_ACCEPTED: f' \
  -H 'X-RequestDigest: 0x089E7....905ED2601,03 Aug 2018 05:12:10 -0000' \
  -H 'X-RequestForceAuthentication: true' \
  -d '<?xml version="1.0" encoding="UTF-8"?>
<Request xmlns="http://schemas.microsoft.com/sharepoint/clientquery/2009" AddExpandoFieldTypeSuffix="true" SchemaVersion="15.0.0.0" LibraryVersion="16.0.7918.1217" ApplicationName="Lucidworks Fusion">
   <Actions>
      <ObjectPath Id="4" ObjectPathId="3" />
      <ObjectPath Id="6" ObjectPathId="5" />
      <Query Id="7" ObjectPathId="5">
         <Query SelectAllProperties="true">
            <Properties>
               <Property Name="NextStartIndexFromSharePoint" ScalarProperty="true" />
            </Properties>
         </Query>
         <ChildItemQuery SelectAllProperties="true">
            <Properties />
         </ChildItemQuery>
      </Query>
   </Actions>
   <ObjectPaths>
      <Constructor Id="3" TypeId="{268004ae-ef6b-4e9b-8425-127220d84719}" />
      <Method Id="5" ParentId="3" Name="GetSitePropertiesFromSharePoint">
         <Parameters>
            <Parameter Type="Null" />
            <Parameter Type="Boolean">false</Parameter>
         </Parameters>
      </Method>
   </ObjectPaths>
</Request>'

The result will contain your site collections.

If you happen to have 1000's site collections in your SharePoint online, you may need to figure out how to change this query to support paging.

Nicholas DiPiazza
  • 717
  • 2
  • 16
  • 42
1

The query that you have used will only return the sites that the user running the code has access to. Looks like you are trying to get the site collections list from O365 tenant. Here is something that you can try using CSOM.

Vipul Kelkar
  • 1,534
  • 2
  • 14
  • 25
  • 2
    If there is a way to do it not using C# ?

    I wanted to make a Java application running on a local client machine (not web server) that will be able to get list of site collections.

    – Alex Aug 29 '14 at 14:14
  • 2
    How do I do that through REST API? Is there a way? – Syed Mauze Rehan Aug 01 '15 at 08:23
1

I know this is an old post but I compare current address to ('https://yourURL/_api/Web/FirstUniqueAncestorSecurableObject').d.Url. If they are equal, I assume I'm talking to the site collection. Please correct me if that is off base but I hope this may help someone in the future.

wjervis
  • 5,738
  • 24
  • 45
  • Yes this is off base. the question was how do you obtain a list of site collections in the entire sharepoint tenant. not "is this site a site collection" – Nicholas DiPiazza May 03 '18 at 05:28