- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
my first API
Hello,
I am trying to learn APIs for the first time whilst also trying to get a specific job done and need a little guidance.
We have some WiFi monitoring and reporting software that can see all the BSSID's flying around, but at the moment it cant give us any clear info about them. In order to do that I need to feed it an xml that will tell it which BSSIDs belong to which AP's (and the associated SSID and the band).
We have hundreds of AP's so going through the meraki portal and copying this data manually is painful - I'm hoping I can pull this data with a GET request?
So far I have setup postman with a new environment and set a API key and baseURL variables.
At this point I wanted to turn to some pre-made examples from here but its v0 not v1
https://documenter.getpostman.com/view/7928889/SVmsVg6K#9019f8d0-a1db-4eb1-b45d-c348dc800e32
will these examples be unusable in v1? If so is there a v1 set of basic API's like this somewhere? In any case I coudlnt find something that provided the data fields I'm after 😞
Also - we have lots of different networks and Id need this info for each network - does that complicate things? IE can you do a API call on all networks or is it per network etc?
Thanks!
Solved! Go to solution.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Quick and dirty example.
I just tested it on an org with over 12000 active AP-BSSIDs (took 2-3 minutes), it works ok.
You'll need Python 3 and the Meraki Python library installed.
Most of the code is setting things up and error detection, the action all happens in just a few lines.
If you want to add extra output fields, just edit the print statement.
import os
import sys
import meraki.aio
import asyncio
#import the org id and api key from the environment
#or you could hard code them, but that's less desirable
ORG_ID = os.environ.get("PARA0")
API_KEY = os.environ.get("PARA1")
async def processAp(aiomeraki: meraki.aio.AsyncDashboardAPI, ap):
try:
# get list of statuses for an AP
statuses = await aiomeraki.wireless.getDeviceWirelessStatus(ap['serial'])
except meraki.AsyncAPIError as e:
print(f'Meraki API error: {e}', file=sys.stderr)
sys.exit(0)
except Exception as e:
print(f'some other error: {e}', file=sys.stderr)
sys.exit(0)
for bss in statuses['basicServiceSets']:
if bss['enabled']:
print(f"{ap['name']},{bss['ssidName']},{bss['bssid']},{bss['band']}")
return
async def main():
async with meraki.aio.AsyncDashboardAPI(
api_key=API_KEY,
base_url='https://api.meraki.com/api/v1/',
print_console=False,
output_log=False,
suppress_logging=True,
wait_on_rate_limit=True,
maximum_retries=100
) as aiomeraki:
#get the wireless devices
try:
aps = await aiomeraki.organizations.getOrganizationDevices(ORG_ID, perPage=1000, total_pages="all", productTypes = ["wireless"])
except meraki.AsyncAPIError as e:
print(f'Meraki API error: {e}', file=sys.stderr)
sys.exit(0)
except Exception as e:
print(f'some other error: {e}', file=sys.stderr)
sys.exit(0)
# process devices concurrently
apTasks = [processAp(aiomeraki, ap) for ap in aps]
for task in asyncio.as_completed(apTasks):
await task
if __name__ == '__main__':
asyncio.run(main())
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
ooh, actually - is this what I need?
https://developer.cisco.com/meraki/api-v1/#!get-device-wireless-status
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
ok cool, just realised that this page will actually run the API for you as well! 😛
It runs ok but I have to specify the serial number of a specific AP. Is there a way to to run it against all AP's in all networks?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
found this - /organizations/{organizationId}/deviceStatuses
now just to figure out where to get teh org id from? I found old posts about a v0 api call but the actual post with an explanation isnt up anymore 😘
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
aah but it doesn't show what i need 😞
need a "connection stats" call for organisation - which I cant find atm
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I use https://developer.cisco.com/meraki/api-v1/#!get-network-wireless-connection-stats
Assuming you want details at SSID level, You need to call it on each SSID on each network (that has at least one AP) in the organization.
To get the list of networks... https://developer.cisco.com/meraki/api-v1/#!get-organization-networks
To see if a network has any APs, check the devices... https://developer.cisco.com/meraki/api-v1/#!get-network-devices
To find the SSIDs in a network... https://developer.cisco.com/meraki/api-v1/#!get-network-wireless-ssids
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I have written plenty of custom applications using the API and what I found to be very useful is to get all your networks and devices (switches, access points) into a back end SQL database. As these do not change very often no point in running APi calls to get this info to pass to another API
I then grab the device info and network ID from database and then run the needed API to do what ever I need to do
Again for monitoring and reporting, I write to a back end SQL database and use PowerBI to create fancy graphs
Example of what I have written
AP channel utilisation. An alert sent out when utilisation is high. A PowerBI report to show AP utilisation over time
AP client connections to diffrent SSID's, or Access Point, again written to back end SQL database and PowerBI reports to see usage over period of time can be grouped into network ID, SSID . An alert sent out if client counts is high so can be proactive as end users complain about performance.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I think an SQL database is a bit much for me at the moment - Id have to install and run it from my laptop (getting it installed on a full blown SQL server might be difficult, esp as we are in teh process of getting rid of as many servers as possible).
Passing info from one API call to another seems ok - is it difficult to do?
I have about 47 networks each with anything from 5 to 100 AP devices : / - I have managed to do an API call that returns the serial numbers of every AP device in all networks - so presumably I just need to pass all of these serial numbers to a
/devices/{serial}/wireless/status
?
thanks!
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
yes that is correct, what you could use instead of SQL is a CSV text file to hold the devices you have and with the API call, read the CSV file and pass the device details to the API
so for example if you want to run API on a switch, only pass the device type that is a switch (see example below of a CSV file format)
<device name>,<device serial number>,<device type>,<device network ID>,<device IP Address>
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
so...I have a json (that I could convert to csv) with the following (iv removed some details for posting here);
{
"mac": "mac ",
"serial": "serial number",
"name": "GD - AP - 2",
"model": "MR33",
"networkId": "networkid",
"orderNumber": "ordernumber",
"claimedAt": "2018-07-30T10:30:08.789690Z",
"tags": [ "recently-added" ],
"productType": "wireless"
},
the file contains hundreds of these - I would need to pull out just the serial numbers into (I guess) a query parameter array - not sure how thats done
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I recommend install Python, version 3.11, then add the Meraki Python library (which handles a lot of low-level stuff for you).
https://www.python.org/downloads/release/python-3110/
https://developer.cisco.com/meraki/api-v1/#!python
It makes working with the API simpler once you get the hang of things.
There are many online sources on learning/using Python. If you get stuck, search with google for what you want to do, i.e. "python array", or "python dictionary" - calls typically return dictionaries, which may have arrays of dictionaries inside them.
Loads of examples here...
https://developer.cisco.com/codeexchange/platforms/meraki#search=meraki&lang=Python
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
yes convert the contents of the json out into a csv file
once you have the data in CSV file, you loop thru the file and pass the serial number as a parameter tto the API call.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
is that something you have to do in python?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
you can write code in Python or any other language
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I think I might be chasing down the wrong route here with the CSV. I think I should clarify my end-goal but not sure if it would be better as new post or keep going here do you think?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
what is your end goal ?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
takes a breath...
ok, so I need a CSV file that contains data for each and every access point in our organisation.
For each AP I need a list of that AP's name and every enabled BSSID with the BSSIDs band and SSID.
I can see I can get the BSSID/band/SSID info from /devices/serialnumber/wireless/status
{
"basicServiceSets": [
{
"ssidName": "Unconfigured SSID 1",
"ssidNumber": 0,
"enabled": false,
"band": "2.4 GHz",
"bssid": "00:00:00:00:00:00",
"channel": 6,
"channelWidth": "20 MHz",
"power": "16 dBm",
"visible": true,
"broadcasting": false
},
{
"ssidName": "Unconfigured SSID 1",
"ssidNumber": 0,
"enabled": false,
"band": "5 GHz",
"bssid": "00:00:00:00:00:00",
"channel": 44,
"channelWidth": "80 MHz",
"power": "19 dBm",
"visible": true,
"broadcasting": false
},
however you have to feed the API the AP serial number. I assume if I tried to feed it all the serial numbers in one API call, the return would just give me all the "basicServiceSets" without letting me know which belongs to which AP.
I can get the AP serial numbers and names from
/organizations/orgID/devices/statuses?productTypes[]=wireless
{
"name": "GD - AP - 2",
"serial": "0000-0000-0000",
"mac": "00:00:00:00:00:00",
"publicIp": "0.0.0.0",
"networkId": "N_0000000000000000",
"status": "online",
"lastReportedAt": "2022-12-07T11:21:44.044000Z",
"productType": "wireless",
"model": "MR33",
"tags": [ "recently-added" ],
"lanIp": "0.0.0.0",
"gateway": "0.0.0.0",
"ipType": "dhcp",
"primaryDns": "0.0.0.0",
"secondaryDns": "0.0.0.0"
},
I am struggling to imagine the necessary workflow. The serial number and name come from one API and all the other info I need come from another API, but the field that links the two API is the serial number which I don't actually need but its the only thing that ties the AP name to the other bits of info : /
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
The workflow would be as follows
The first API calls would be to get all the AP's serial number and save these details to a text file, so example of your text file could be
xxxxxx
yyyyyy
zzzzzz
aaaaa
bbbbb
cccccc
So call /organizations/orgID/devices/statuses?productTypes[]=wireless
once you have got this the next API call would be passing the serial number of the AP to the API call
You would read the text file one line at a time and once you have the data in json format, process this to extract the necessary details, write it to a text file and move onto the next serial number in the text file .
You are basically looping thru the text file, reading the serial number calling the API to get the data you need
Hope this makes sense
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
thanks,
so, would I be doing a single API call per serial number?
Since the data returned from /devices/serialnumber/wireless/status doesn't actually include the serial number or AP name, how would I link the API return data to the serial number that was used in the call?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
@Adrian4 wrote:thanks,
so, would I be doing a single API call per serial number?
Since the data returned from /devices/serialnumber/wireless/status doesn't actually include the serial number or AP name, how would I link the API return data to the serial number that was used in the call?
once you export the data from the 2nd API call, where will this data be stored/saved ?
As I stated earlier, I write to a back end SQL database and this is how I would link the AP name or serial number to the data extracted. For your case you could write it to a text file with the AP serial number or name the header of the text file.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Quick and dirty example.
I just tested it on an org with over 12000 active AP-BSSIDs (took 2-3 minutes), it works ok.
You'll need Python 3 and the Meraki Python library installed.
Most of the code is setting things up and error detection, the action all happens in just a few lines.
If you want to add extra output fields, just edit the print statement.
import os
import sys
import meraki.aio
import asyncio
#import the org id and api key from the environment
#or you could hard code them, but that's less desirable
ORG_ID = os.environ.get("PARA0")
API_KEY = os.environ.get("PARA1")
async def processAp(aiomeraki: meraki.aio.AsyncDashboardAPI, ap):
try:
# get list of statuses for an AP
statuses = await aiomeraki.wireless.getDeviceWirelessStatus(ap['serial'])
except meraki.AsyncAPIError as e:
print(f'Meraki API error: {e}', file=sys.stderr)
sys.exit(0)
except Exception as e:
print(f'some other error: {e}', file=sys.stderr)
sys.exit(0)
for bss in statuses['basicServiceSets']:
if bss['enabled']:
print(f"{ap['name']},{bss['ssidName']},{bss['bssid']},{bss['band']}")
return
async def main():
async with meraki.aio.AsyncDashboardAPI(
api_key=API_KEY,
base_url='https://api.meraki.com/api/v1/',
print_console=False,
output_log=False,
suppress_logging=True,
wait_on_rate_limit=True,
maximum_retries=100
) as aiomeraki:
#get the wireless devices
try:
aps = await aiomeraki.organizations.getOrganizationDevices(ORG_ID, perPage=1000, total_pages="all", productTypes = ["wireless"])
except meraki.AsyncAPIError as e:
print(f'Meraki API error: {e}', file=sys.stderr)
sys.exit(0)
except Exception as e:
print(f'some other error: {e}', file=sys.stderr)
sys.exit(0)
# process devices concurrently
apTasks = [processAp(aiomeraki, ap) for ap in aps]
for task in asyncio.as_completed(apTasks):
await task
if __name__ == '__main__':
asyncio.run(main())
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
thank you so much! this looks great. I cant test it at the moment as I'm having trouble installing the meraki module, but thats an entirely different problem.
Thanks again!
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
sorry, just one more follow up question....
it looks like you have put your key and org id in a module called "os"?
Where do you put modules you make like this? I am trying to use PyCharm but I'm a bit lost with the interface and where things should go, esp if they are modules I want to call in scripts.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
The 'os' module is part of the base Python install, it does a bunch of things, in this case I'm using it to get the value of runtime environment variables.
I'm running on Unix-like systems, so I pull in environment variables from the shell, PARA0 and PARA1. Of course you can choose your own names, there is nothing special about these names, they just need to match the names you use in the environment.
In the shell that runs the script, I set these two environment variable like this...
PARA0="the org ID value"
PARA1="the API key value"
...then the script can pull in the values by variable name.
If you are on windows, you can set environment variables with the same names and it should work just the same, the way you set them depends where you are running the scripts from, there are some examples here of using Windows variables with scripts with the 'os' module...
https://www.twilio.com/blog/environment-variables-python
In general, if you install modules using pip, I think they should go in the right place automatically, for instance...
pip install meraki
...or to update the version...
pip install --upgrade meraki
...but I don't use Python on Windows so maybe there are some other/better ways!
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Hey sungod, this was very helpful, thank you! Did you build this script from scratch or did you use documentation to assist?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
It's mostly from adapting from another script I wrote, the basics of set-up, iterating by something, error handling etc. are common across scripts, so whenever I do something new I tend to see which old script I have that is closest in structure to what I need and then adapt it, and I'm lazy 😀
This one is using async I/O as it is generally more efficient, but it does affect readability.
If you look on the github site, there are a few examples you can use as starting points to experiment...
https://github.com/meraki/dashboard-api-python/tree/main/examples
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I apreciate you sharing that. Thanks again!
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
PS- I used ChatGPT. I too start of my base script of calling up all my networks, and go from there.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I have just a bit of code here: jadexing/Merakicode: Code for Meraki Wi-Fi (github.com)
All mine is Wi-Fi based.
I open Powershell, go to the script directory and execute.
